π νμ¬ μν© λ° λ°°κ²½ μ€λͺ
π½ SecurityConfig API μμ² νλ¦
API μμ²μ κ³΅κ° μ κ·Ό νν° μ²΄μΈκ³Ό μΈμ¦μ΄ νμν νν° μ²΄μΈμ μμ°¨μ μΌλ‘ κ±°μΉλ€.
- κ³΅κ° μ κ·Ό νν° μ²΄μΈ: λ‘κ·ΈμΈ μμ΄λ μ κ·Ό κ°λ₯ν μλν¬μΈνΈλ₯Ό μ²λ¦¬νλ€.
- μΈμ¦μ΄ νμν νν° μ²΄μΈ: λ‘κ·ΈμΈμ΄ λ°λμ μꡬλλ μλν¬μΈνΈλ₯Ό μ²λ¦¬νλ€.
π½ νμ μ 보 μ‘°ν μλν¬μΈνΈ
νμ¬ νλ‘μ νΈμμλ νμ μ 보 μ‘°νλ₯Ό μν λ κ°μ§ APIλ₯Ό μ 곡νλ€.
- λμ κ²½λ‘ API: νΉμ νμ μ 보λ₯Ό μ‘°ννλ APIλ‘, @PathVariableμ μ¬μ©νμ¬ memberId κ°μ λμ μΌλ‘ μ λ¬λ°λλ€.
- μ μ κ²½λ‘ API: μμ μ μ 보λ₯Ό μ‘°ννλ APIλ‘, λ¬Έμμ΄ "self"λ₯Ό μ μ μΌλ‘ κ²½λ‘μ μ¬μ©νλ€.
μ λ κ²½λ‘λ μλ‘ λ€λ₯Έ μν μ μννμ§λ§, Spring MVCμ URL 맀ν λ°©μ λλ¬Έμ κ²½λ‘ μΆ©λμ΄ λ°μν κ°λ₯μ±μ΄ μλ€. μ μ κ²½λ‘λ λ¬Έμμ΄λ‘ κ³ μ λμ΄ μλ λ°λ©΄, λμ κ²½λ‘λ memberIdλΌλ λ³μλ‘ μ μλμ΄ μκΈ° λλ¬Έμ selfκ° λμ κ²½λ‘μ μΌλΆλ‘ μΈμλμ΄ μΆ©λμ΄ λ°μν μ μλ€.
⢠ꡬν μ½λ
π½ SecurityConfig ν΄λμ€
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
// κ³΅κ° μ κ·Ό endpoint
private RequestMatcher[] publicRequestMatchers() {
List<RequestMatcher> requestMatchers = List.of(
antMatcher(GET, "/members/{memberId}"),
...
);
return requestMatchers.toArray(RequestMatcher[]::new);
}
// μΈμ¦ λ° κΆνμ΄ νμν endpoint
private RequestMatcher[] authenticatedRequestMatchers() {
List<RequestMatcher> requestMatchers = List.of(
antMatcher(GET, "/members/self"),
...
);
return requestMatchers.toArray(RequestMatcher[]::new);
}
...
}
π½ MemberController ν΄λμ€
@RestController
@RequestMapping("members")
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@GetMapping("/self")
public ResponseEntity<BfResponse<MemberInfoResponse>> getSelf(
@AuthenticationPrincipal PrincipalDetails principalDetails
) {
MemberInfoResponse memberInfoResponse = memberService.getMember(Long.parseLong(principalDetails.getUsername()));
return ResponseEntity.ok(new BfResponse<>(memberInfoResponse));
}
@GetMapping("/{memberId}")
public ResponseEntity<BfResponse<MemberInfoResponse>> getMember(
@PathVariable Long memberId
) {
MemberInfoResponse memberInfoResponse = memberService.getMember(memberId);
return ResponseEntity.ok(new BfResponse<>(memberInfoResponse));
}
...
}
π¨ λ¬Έμ μν©
/members/self κ²½λ‘κ° /members/{memberId} κ²½λ‘μ μΆ©λνμ¬, μ μ κ²½λ‘λ‘ λ§€νλμ§ μκ³ λμ κ²½λ‘λ‘ μλͺ» μΈμλλ λ¬Έμ κ° λ°μνλ€. μ΄λ‘ μΈν΄, "self"κ° memberId λ³μλ‘ μ²λ¦¬λμ΄ /members/{memberId} κ²½λ‘μ λ‘μ§μΌλ‘ μ λ¬λμλ€. νμ§λ§, λ‘μ§ μ memberIdλ μ«μλ§ νμ©λλ―λ‘, "self" λ¬Έμμ΄μ μ²λ¦¬ν μ μμ΄ μλ¬κ° λ°μνλ€.
βοΈ μμΈ λΆμ
λμ κ²½λ‘μ μ μ κ²½λ‘μ λν΄ μμ§ λͺ»νλ νμ, μ΄λ² νΈλ¬λΈμν κ³Όμ μμ λ€μ ν€λ§Έλ€.
βΆ μ members/selfλ§ μλ¬κ° λ°μνμκΉ?
- Security μΈμ¦ νν°μμ members/{memberId}κ° κ³΅κ° νν° μ²΄μΈμμ μ°μ μ²λ¦¬λμ΄, members/selfκ° λμνμ§ μλ κ±ΈκΉ?
- selfλ₯Ό κ³΅κ° μ κ·Ό 체μΈμμ λ¨Όμ μ μΈν΄λ λμΌν μλ¬κ° λ°μνλ€.
- self, me, myκ° {memberId}λ‘ μΈμλλ κ±ΈκΉ?
- λ€λ₯Έ λ¬Έμμ΄(current, my-profile λ±)μ μ¬μ©ν΄ ν μ€νΈνμ λλ λμΌν μλ¬κ° λ°μνλ€. μ΄λ₯Ό ν΅ν΄ λ¨μν νΉμ λ¬Έμμ΄μ λ¬Έμ λΌκΈ°λ³΄λ€λ, μ μ κ²½λ‘μ λμ κ²½λ‘μ μ²λ¦¬ λ°©μμ λ¬Έμ κ° μμμ κ²μνλ©΄μ μκ² λμλ€.
- λμ κ²½λ‘μ μ μ κ²½λ‘κ° μΆ©λμ΄ λλ κ±ΈκΉ?
- λ¨μν μ°μ μμ μΆ©λ λ¬Έμ λ μλμλ€. Springμ΄ /members/selfλ₯Ό μ μ κ²½λ‘λ‘ μΈμνμ§ μκ³ , λμ κ²½λ‘ /members/{memberId}μ μΌλΆλ‘ ν΄μνκΈ° λλ¬Έμ΄λ€. κ²°κ³Όμ μΌλ‘ "self"λΌλ λ¬Έμμ΄μ΄ memberId λ³μλ‘ μ λ¬λμ΄, /members/{memberId} κ²½λ‘μ λ©μλλ‘ λ§€νλλ©΄μ λ¬Έμ κ° λ°μνλ€.
Spring URL 맀ν μ°μ μμ
1. μ μ κ²½λ‘: μ νν μΌμΉνλ κ²½λ‘
2. λμ κ²½λ‘: {}μ κ°μ λ³μλ‘ μ μλ κ²½λ‘
3. μμΌλμΉ΄λ κ²½λ‘: *(λ¨μΌ κ²½λ‘) λλ **(λ€μ€ κ²½λ‘)λ₯Ό ν¬ν¨ν κ²½λ‘
π¨ ν΄κ²° λ°©λ²
βΆ 1. μ μ κ²½λ‘μ λμ κ²½λ‘ λͺ νν ꡬλΆ
/members/selfλ μ μ κ²½λ‘λ‘, /members/id/{memberId}λ λμ κ²½λ‘λ‘ μ μνμ¬ κ²½λ‘λ₯Ό λͺ νν λΆλ¦¬νλ€.
π½ ꡬν μ½λ
@RestController
@RequestMapping("members")
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@GetMapping("/self")
public ResponseEntity<BfResponse<MemberInfoResponse>> getSelf( ... ) { ... }
@GetMapping("/id/{memberId}") // λμ κ²½λ‘ λͺ
νν ꡬλΆ
public ResponseEntity<BfResponse<MemberInfoResponse>> getMember( ... ) { ... }
...
}
βΆ 2. 컨νΈλ‘€λ¬μμ μ κ· ννμμΌλ‘ λμ κ²½λ‘ μ ν
Spring 곡μ λ¬Έμμμ κΆμ₯νλ λ°©μμΌλ‘, 컨νΈλ‘€λ¬μμ λμ κ²½λ‘μ κ°μ μ κ· ννμμΌλ‘ μ ννμ¬ μμμΉ λͺ»ν λ¬Έμμ΄ λ§€μΉμ λ°©μ§ν μ μλ€. μ΄λ₯Ό ν΅ν΄ λμ κ²½λ‘μ μ μ κ²½λ‘ κ° μΆ©λ λ¬Έμ λ₯Ό ν΄κ²°ν μ μλ€.
π½ ꡬν μ½λ
@RestController
@RequestMapping("members")
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@GetMapping("/self")
public ResponseEntity<BfResponse<MemberInfoResponse>> getSelf( ... ) { ... }
@GetMapping("/members/{memberId:[0-9]+}") // λμ κ²½λ‘λ₯Ό μ κ· ννμμΌλ‘ μ ν
public ResponseEntity<BfResponse<MemberInfoResponse>> getMember( ... ) { ... }
...
}
π½ λ¬Έμ μ
νμ¬ λ‘μ§μμλ APIλ₯Ό κ°λ°ν λ Spring Securityλ₯Ό ν΅ν΄ λͺ¨λ μλν¬μΈνΈλ₯Ό λ±λ‘ν΄μΌ νλ―λ‘, μ΄ λ°©λ²λ§μΌλ‘λ λ¬Έμ λ₯Ό ν΄κ²°ν μ μμλ€.
βΆ 3. Spring Securityμμ μ κ· ννμ λμ κ²½λ‘ λͺ μμ λ±λ‘
Spring 곡μ λ¬Έμμμ λͺ μμ λ±λ‘(Explicit Registrations)μ΄λΌλ κ°λ μ μκ² λμλ€. μ΄λ₯Ό 보며 SecurityConfigμμλ μ κ· ννμμ νμ©νμ¬ λ¬Έμ λ₯Ό ν΄κ²°ν μ μκ² λ€λ μκ°μ΄ λ€μλ€. μ΄ν Spring Security μ€μ μμ λμ κ²½λ‘μ νμ©λλ κ°μ μ κ· ννμμΌλ‘ μ ννλ λ°©λ²μ μ μ©νμ¬ λ¬Έμ λ₯Ό ν΄κ²°ν μ μμλ€.
π½ ꡬν μ½λ
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
// κ³΅κ° μ κ·Ό endpoint
private RequestMatcher[] publicRequestMatchers() {
List<RequestMatcher> requestMatchers = List.of(
antMatcher(GET, "/members/{memberId:\\d+}"), // λμ κ²½λ‘λ₯Ό μ κ· ννμμΌλ‘ μ ν
...
);
return requestMatchers.toArray(RequestMatcher[]::new);
}
// μΈμ¦ λ° κΆνμ΄ νμν endpoint
private RequestMatcher[] authenticatedRequestMatchers() {
List<RequestMatcher> requestMatchers = List.of(
antMatcher(GET, "/members/self"),
...
);
return requestMatchers.toArray(RequestMatcher[]::new);
}
...
}
π κ²°κ³Ό κ΄μ°°
- /members/self κ²½λ‘κ° λμ μΌλ‘ 맀μΉλμ§ μκ³ μ μ μΌλ‘ 맀μΉλμλ€.
- /members/{memberId} κ²½λ‘λ μ¬μ ν λμ μΌλ‘ λμνλ©°, κ²½λ‘ κ° μΆ©λμ΄ λ°μνμ§ μμλ€.
π‘ κ³ μ°°
- μ΄λ² νΈλ¬λΈμν μ ν΅ν΄ Spring MVCμ λμ κ²½λ‘μ μ μ κ²½λ‘ λ§€νμ λν κ°λ μ λ°°μ λ€. μ΄μ μλ ν΄λΉ κ°λ μ μμ§ λͺ»νκ³ APIλ₯Ό κ°λ°νμκΈ° λλ¬Έμ, μ΄ κ²½νμ ν΅ν΄ μλ―Έ μλ μ§μμ μ΅λνκ² λ κ² κ°μ λ§€μ° κΈ°λ»€λ€.
- νμ§λ§ μ΄ν λͺ¨μ κ΄λ ¨ APIμμ /meetings/{meetingId}μ /meetings/{organizationId}μ²λΌ λΉμ·ν νμμ λμ κ²½λ‘κ° νμν λ κ²½λ‘ κ° μΆ©λ λ¬Έμ κ° λ°μνλ€. μ΄λ² μ¬λ‘λ /members/selfμ²λΌ λ¬Έμμ΄λ‘ μ²λ¦¬ν μ μλ μλν¬μΈνΈμκΈ° λλ¬Έμ, /members/{memberId}μμ memberIdλ₯Ό μ«μλ‘ μ ννλ λ°©μμΌλ‘ ν΄κ²°ν μ μμλ€. κ·Έλ¬λ /meetings/{meetingId}μ /meetings/{organizationId}λ ꡬ쑰μ μΌλ‘ κ²½λ‘κ° λΉμ·ν΄ μ΄ λ°©μμ΄ μ μ©λμ§ μμλ€.
- κ²°κ΅, ν΄κ²° λ°©λ² 1λ²μ μ μ©νμ¬ /meetings/organization/{organizationId}λ‘ λ³κ²½ν΄ λ¬Έμ λ₯Ό ν΄κ²°νλ€. μ΄ κ²½νμ ν΅ν΄ RESTful API μ€κ³ μμΉμ λν΄ λ κΉμ 곡λΆμ μ΄ν΄κ° νμνλ€λ μ μ λκΌλ€.
π μ°Έκ³ μλ£
'TroubleShooting' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[TroubleShooting] Jacksonμ΄ μ«μ΄νλ λ¨μΌ νλ (1) | 2024.12.04 |
---|---|
[TroubleShooting] μ£Όλμ΄ κ°λ°μλ€μ μΈκ² λ§λλ CORS μλ¬ (0) | 2024.12.03 |
[TroubleShooting] Cascade μ΅μ μ λΆλͺ¨μκ²λ§ (0) | 2024.10.22 |
[TroubleShooting] 컬λ μ νλ μ΄κΈ°νκ° λμ§ μλ Builder ν¨ν΄ (0) | 2024.10.22 |
[TroubleShooting] DB 쿼리 λΉμ© (0) | 2024.10.04 |