๐Ÿ” ์ปจํŠธ๋กค๋Ÿฌ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์œ ์ € ID ์•ˆ์ „ํ•˜๊ฒŒ ์ฃผ์ž…ํ•˜๊ธฐ

๋ฐ•์ค€ํ˜•ยท2025๋…„ 8์›” 31์ผ

์Šคํ”„๋ง ๊ฐœ๋ฐœ

๋ชฉ๋ก ๋ณด๊ธฐ
18/20
post-thumbnail

์ด ๊ธ€์€ ์œ ์ € ์‹๋ณ„์ž๋ฅผ ์ปจํŠธ๋กค๋Ÿฌ์— ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ์‹์„ ํ—ค๋” ์ง์ ‘ ํŒŒ์‹ฑ โ†’ SecurityContext ์ปจํŠธ๋กค๋Ÿฌ๋งˆ๋‹ค ์ง์ ‘ ์ ‘๊ทผ โ†’ @CurrentUserId + HandlerMethodArgumentResolver ์ˆœ์„œ๋กœ ๊ฐœ์„ ํ•ด ์˜จ ๊ณผ์ •์„ ์ •๋ฆฌํ•˜๊ณ , ์ตœ์ข… ์„ค๊ณ„์™€ ์ ์šฉ ํšจ๊ณผ๋ฅผ ์ œ์‹œํ•œ๋‹ค. ๋ถˆํ•„์š”ํ•œ ์ค‘๋ณต๊ณผ ๋ณด์•ˆ/์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ถˆ์ผ์น˜๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ์ปจํŠธ๋กค๋Ÿฌ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋‹จ์ˆœํ™”ํ•˜๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ์ด๋‹ค.


โœ๏ธ 1. ์™œ ๋ฐ”๊ฟจ๋‚˜

  • ์ค‘๋ณต ๋กœ์ง์ด ๋ˆ„์ ๋œ๋‹ค: ์ปจํŠธ๋กค๋Ÿฌ/์„œ๋น„์Šค๋งˆ๋‹ค ํ† ํฐ ํŒŒ์‹ฑ๊ณผ ์˜ˆ์™ธ ๋ถ„๊ธฐ๊ฐ€ ๋ฐ˜๋ณต๋œ๋‹ค.
  • ์ •์ฑ… ๋ถˆ์ผ์น˜๊ฐ€ ์ƒ๊ธด๋‹ค: 401/403, ๋งŒ๋ฃŒ/์œ„์กฐ ๊ตฌ๋ถ„, ๋กœ๊ทธ ํฌ๋งท์ด ์ œ๊ฐ๊ฐ์ด ๋œ๋‹ค.
  • ๊ฐ€๋…์„ฑ/ํ…Œ์ŠคํŠธ ๋น„์šฉ์ด ์ฆ๊ฐ€ํ•œ๋‹ค: ํ•ต์‹ฌ ๋กœ์ง์ด ์ธ์ฆ ๋ณด์กฐ ๋กœ์ง์— ๋ฌปํž˜.
  • ๋ฌธ์„œํ™” ํ˜ผ์„ ์ด ๋ฐœ์ƒํ•œ๋‹ค: ์–ด๋–ค๊ฑด Swagger์— userId๊ฐ€ ๋…ธ์ถœ๋˜์–ด ์žˆ๊ณ , ์–ด๋˜ ๊ฑด ํ† ํฐ์—์„œ ์ถ”์ถœํ•˜๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ์Œ.

๋ชฉํ‘œ๋Š” ๋‹จ์ˆœํ•˜๋‹ค. ์œ ์ € ID๋Š” ์ž๋™์œผ๋กœ, ์•ˆ์ „ํ•˜๊ฒŒ, ์ผ๊ด€๋˜๊ฒŒ ์ฃผ์ž…ํ•˜๊ณ , ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค์—๋งŒ ์ง‘์ค‘ํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค.



๐Ÿ” 2. ๋‹จ๊ณ„๋ณ„ ๋ณ€ํ™”

A) ํ—ค๋”์—์„œ ์ง์ ‘ ์ถ”์ถœ

  • ๋ฐฉ์‹: Authorization: Bearer ...์—์„œ ํ† ํฐ์„ ์ถ”์ถœยท๊ฒ€์ฆํ•ด userId๋ฅผ ์–ป๋Š”๋‹ค.
  • ํ•œ๊ณ„: ๋ชจ๋“  ์—”๋“œํฌ์ธํŠธ์— ์œ ์‚ฌ ์ฝ”๋“œ๊ฐ€ ํฉ์–ด์ง€๊ณ , ๋งค๋ฒˆ ํ—ค๋” ํŒŒ์‹ฑ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์˜ˆ์™ธยท๋กœ๊ทธ ํ‘œ์ค€ํ™”๊ฐ€ ์–ด๋ ต๋‹ค.

B) SecurityContext์—์„œ ์ง์ ‘ ๊บผ๋ƒ„

  • ๋ฐฉ์‹: JWT ํ•„ํ„ฐ์—์„œ ๊ฒ€์ฆ ํ›„ Authentication์„ ์„ธํŒ…ํ•˜๊ณ , ์ปจํŠธ๋กค๋Ÿฌ/์„œ๋น„์Šค์—์„œ SecurityContextHolder๋กœ userId๋ฅผ ์ฝ๋Š”๋‹ค. ๊ณต์šฉ ์œ ํ‹ธ SecurityContextProvider๋กœ ๊ฐ์‹ผ๋‹ค.
  • ํšจ๊ณผ: ๊ฐ API ๋ฉ”์„œ๋“œ๋งˆ๋‹ค ์žˆ๋˜ ํ—ค๋” ํŒŒ์‹ฑ ์ฝ”๋“œ๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ  ์ธ์ฆ ์‹คํŒจ ์ฒ˜๋ฆฌ๊ฐ€ ํ•œ ๊ณณ์œผ๋กœ ๋ชจ์ธ๋‹ค.
  • ํ•œ๊ณ„: ๋ฉ”์„œ๋“œ๋งˆ๋‹ค ์œ ํ‹ธ ํ˜ธ์ถœ์ด ๋ฐ˜๋ณต๋˜๊ณ , ์‹œ๊ทธ๋‹ˆ์ฒ˜๋งŒ ๋ณด๊ณ  ์ธ์ฆ ์˜์กด์„ฑ์„ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

C) @CurrentUserId + ๋ฆฌ์กธ๋ฒ„ (์ตœ์ข… ์„ค๊ณ„)

  • ๋ฐฉ์‹: ํŒŒ๋ผ๋ฏธํ„ฐ์— @CurrentUserId Long userId๋งŒ ์„ ์–ธํ•˜๋ฉด ๋ฆฌ์กธ๋ฒ„๊ฐ€ SecurityContext์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์ฃผ์ž…ํ•œ๋‹ค.
  • ํšจ๊ณผ: ๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ โ€œ๋กœ๊ทธ์ธ ํ•„์š”โ€๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๊ณ , ์ธ์ฆ ํŒŒ์ƒ ๋กœ์ง์ด ์™„์ „ํžˆ ๋ถ„๋ฆฌ๋œ๋‹ค.


๐Ÿงฉ 3. ์ตœ์ข… ์„ค๊ณ„

์•„๋ž˜๋Š” ์ตœ์ข… ์„ค๊ณ„์˜ ํ•ต์‹ฌ ์ฝ”๋“œ์™€ ํ•จ๊ป˜, ๊ฐ ํŒŒ์ผ(ํด๋ž˜์Šค/์„ค์ •/์ปจํŠธ๋กค๋Ÿฌ)์— ๋Œ€ํ•ด โ€œ๋ฌด์—‡์„/์™œ/์–ด๋–ป๊ฒŒ/ํ™•์žฅ ํฌ์ธํŠธ/์ฃผ์˜์ โ€์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ•˜์˜€๋‹ค.


3.1 โœจ ์• ๋…ธํ…Œ์ด์…˜: @CurrentUserId

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurrentUserId {}

๋ฌด์—‡์„ ํ•˜๋‚˜

  • ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆ˜์ค€์—์„œ โ€œ์—ฌ๊ธฐ์—๋Š” ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์˜ ID๊ฐ€ ์ฃผ์ž…๋˜์–ด์•ผ ํ•œ๋‹คโ€๋Š” ์˜๋„๋ฅผ ์„ ์–ธํ•œ๋‹ค.

์™œ ์ด๋ ‡๊ฒŒ ํ•˜๋‚˜

  • ElementType.PARAMETER๋กœ ํƒ€๊นƒ์„ ์ œํ•œํ•ด ์ž˜๋ชป๋œ ์‚ฌ์šฉ์„ ์ค„์ธ๋‹ค. (ํ•„๋“œ/๋ฉ”์„œ๋“œ์— ๋ถ™๋Š” ์‹ค์ˆ˜๋ฅผ ๋ด‰์‡„ํ•ด๋ฒ„๋ฆผ)
  • RetentionPolicy.RUNTIME์œผ๋กœ ๋ฆฌํ”Œ๋ ‰์…˜ ์‹œ์ ์— ๋ฆฌ์กธ๋ฒ„๊ฐ€ ์• ๋…ธํ…Œ์ด์…˜์„ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค. -> ๋Ÿฐํƒ€์ž„ ์‹œ์ ๊นŒ์ง€ ๋Œ๊ณ ๊ฐ
  • ์ž์ฒด ์†์„ฑ์ด ์—†๋Š” marker annotation์œผ๋กœ ๋‹จ์ˆœ/๋ช…ํ™•ํ•˜๊ฒŒ ์˜๋„๋ฅผ ํ‘œํ˜„ํ•œ๋‹ค.

๋™์ž‘ ๋ฐฉ์‹ ์š”์•ฝ

  • ์Šคํ”„๋ง MVC๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•ด์„ํ•  ๋•Œ ๋“ฑ๋ก๋œ HandlerMethodArgumentResolver์—๊ฒŒ โ€œ์ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋„ค๊ฐ€ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‚˜?โ€๋ฅผ ์งˆ์˜ํ•œ๋‹ค.
  • supportsParameter()๊ฐ€ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด resolveArgument()๊ฐ€ ํ˜ธ์ถœ๋˜์–ด userId๋ฅผ ์ฑ„์›Œ ๋„ฃ๋Š”๋‹ค.

์ฃผ์˜์ 

  • ์• ๋…ธํ…Œ์ด์…˜ ์ž์ฒด์—๋Š” ์•„๋ฌด ์ •์ฑ…๋„ ๋‹ด์ง€ ์•Š๋Š”๋‹ค. ์ •์ฑ…์€ ๋ฆฌ์กธ๋ฒ„๊ฐ€ ์ฑ…์ž„์ง„๋‹ค.

3.2 ๐Ÿง  ๋ฆฌ์กธ๋ฒ„: CurrentUserIdArgumentResolver

@Component
public class CurrentUserIdArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter p) {
        return p.getParameterAnnotation(CurrentUserId.class) != null
            && p.getParameterType().equals(Long.class);
    }
    @Override
    public Object resolveArgument(MethodParameter p, ModelAndViewContainer m,
                                  NativeWebRequest w, WebDataBinderFactory b) {
        try {
            return SecurityContextProvider.getAuthenticatedUserId();
        } catch (Exception e) {
            throw new CommonException(CommonErrorStatus.UNAUTHORIZED);
        }
    }
}

๋ฌด์—‡์„ ํ•˜๋‚˜

  • @CurrentUserId๊ฐ€ ๋ถ™์€ Long ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ์ง€ํ•˜๊ณ , SecurityContext์—์„œ ๊ฒ€์ฆ๋œ userId๋ฅผ ์ถ”์ถœํ•ด ์ฃผ์ž…ํ•œ๋‹ค.
  • ์ธ์ฆ ์‹คํŒจ/๋ถ€์žฌ ์‹œ ์ผ๊ด€๋œ 401 ์‘๋‹ต์„ ๋ณด์žฅํ•œ๋‹ค.

์™œ ์ด๋ ‡๊ฒŒ ํ•˜๋‚˜

  • ํƒ€์ž…์„ Long์œผ๋กœ ์—„๊ฒฉํžˆ ๊ณ ์ •ํ•ด API ํ‘œ๋ฉด์„ ๋ช…ํ™•ํžˆ ์œ ์ง€ํ•œ๋‹ค. (์ปจํŠธ๋กค๋Ÿฌ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋งŒ ๋ด๋„ ๋ฌด์—‡์ด ๋“ค์–ด์˜ค๋Š”์ง€ ๋ถ„๋ช…ํ•ด์ง„๋‹ค)

๋™์ž‘ ์ˆœ์„œ

1) ์Šคํ”„๋ง MVC๊ฐ€ ํŒŒ๋ผ๋ฏธํ„ฐ ํ•ด์„ ์ค‘ supportsParameter()๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
2) ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋ฉด resolveArgument()๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
3) SecurityContextProvider.getAuthenticatedUserId()๊ฐ€ ๋‚ด๋ถ€์—์„œ Authentication์˜ principal(์˜ˆ: CustomUserDetails)์—์„œ userId๋ฅผ ์ฝ์–ด์˜จ๋‹ค.
4) ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ 401๋กœ ํ†ต์ผํ•œ๋‹ค.

ํ™•์žฅ ํฌ์ธํŠธ

  • ์„ ํƒ ์ธ์ฆ API๋ฅผ ์œ„ํ•ด Optional<Long> ๋˜๋Š” @Nullable Long์„ ํ—ˆ์šฉํ•˜๋Š” ๋‘ ๋ฒˆ์งธ ๋ฆฌ์กธ๋ฒ„๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์‹คํŒจ๋ฅผ 401๋กœ ๋˜์ง€์ง€ ์•Š๊ณ  null์„ ์ฃผ์ž…ํ•œ๋‹ค. -> ์ต๋ช… ์œ ์ € ์ฒ˜๋ฆฌ ๊ฐ™์€ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์ž‡์Œ.

๋ณด์•ˆ/์„ฑ๋Šฅ ๊ณ ๋ ค

  • SecurityContext ์ ‘๊ทผ์€ ์Šค๋ ˆ๋“œ ๋ฐ”์šด๋“œ์ด๋ฏ€๋กœ, ๋น„๋™๊ธฐ ํ˜ธ์ถœ ์‹œ ์ปจํ…์ŠคํŠธ ์ „ํŒŒ ์„ค์ •์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ฆฌ์กธ๋ฒ„ ์ž์ฒด๋Š” ๊ฐ€๋ณ๊ณ , ์š”์ฒญ๋‹น 1ํšŒ ์ •๋„ ํ˜ธ์ถœ๋˜๋ฏ€๋กœ ๋ณ‘๋ชฉ์ด ๋˜์ง€ ์•Š๋Š”๋‹ค.

์ฃผ์˜์ 

  • ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์ด Long์ด ์•„๋‹ˆ๋ฉด ๋ฆฌ์กธ๋ฒ„๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ž„์˜๋กœ userId๋ฅผ ์ž…๋ ฅ๋ฐ›๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ถ”๊ฐ€๋กœ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค. -> ์ž๋™์œผ๋กœ ํ•ด์คŒ.

3.3 โš™๏ธ WebMvc ์„ค์ •: WebMvcConfig

@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
    private final CurrentUserIdArgumentResolver currentUserIdArgumentResolver;
   
    @Override public void addArgumentResolvers(
    	List<HandlerMethodArgumentResolver> rs
	) { 
    	rs.add(currentUserIdArgumentResolver); 
    }
}

๋ฌด์—‡์„ ํ•˜๋‚˜

  • CurrentUserIdArgumentResolver๋ฅผ MVC์— ๋“ฑ๋กํ•ด ์ฃผ์ž…์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•œ๋‹ค.

์™œ ์ด๋ ‡๊ฒŒ ํ•˜๋‚˜

  • ๋ฆฌ์กธ๋ฒ„๋Š” MVC ์ƒ๋ช…์ฃผ๊ธฐ์— ๋“ฑ๋ก๋˜์–ด์•ผ ๋™์ž‘ํ•œ๋‹ค.

๋™์ž‘

  • addArgumentResolvers์—์„œ ๋ฆฌ์กธ๋ฒ„๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์Šคํ”„๋ง์ด ๋งค ์š”์ฒญ๋งˆ๋‹ค ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•ด๋‹น ๋ฆฌ์กธ๋ฒ„์— ์งˆ์˜ํ•œ๋‹ค.

3.4 ๐Ÿงพ ์ปจํŠธ๋กค๋Ÿฌ ์˜ˆ: ์—…๋กœ๋“œ API

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<SuccessResponse<Void>> uploadData(
        @Parameter(hidden = true)
        @CurrentUserId Long userId,
        
        @RequestPart("dataFile")
        MultipartFile dataFile,
        
        @RequestPart(value = "thumbnailFile", required = false)
        MultipartFile thumbnailFile,
        
        @RequestPart @Validated
        UploadDataWebRequest webRequest
) { /* ... */ }

๋ฌด์—‡์„ ํ•˜๋‚˜

  • ์ธ์ฆ ํŒŒ์ƒ ๋ฐ์ดํ„ฐ(userId)๋Š” ์ž๋™ ์ฃผ์ž…ํ•˜๊ณ , ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ์ž…๋ ฅ๋งŒ ๋ฐ›๋Š”๋‹ค.
  • ๋ฉ€ํ‹ฐํŒŒํŠธ ๋ณธ๋ฌธ์—์„œ ํŒŒ์ผ(dataFile, thumbnailFile)๊ณผ JSON DTO(webRequest)๋ฅผ ํ•จ๊ป˜ ๋ฐ›๋Š”๋‹ค.

์™œ ์ด๋ ‡๊ฒŒ ํ•˜๋‚˜

  • โ€œ์ปจํŠธ๋กค๋Ÿฌ ์‹œ๊ทธ๋‹ˆ์ฒ˜ = API ์ŠคํŽ™โ€ ์›์น™์„ ์ง€ํ‚จ๋‹ค.
    @CurrentUserId๋กœ ๋กœ๊ทธ์ธ ํ•„์š”์„ฑ์ด ์‹œ๊ทธ๋‹ˆ์ฒ˜์— ๋“œ๋Ÿฌ๋‚˜๊ณ , userId๋ฅผ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ž„์˜ ์ œ๊ณตํ•˜์ง€ ๋ชปํ•˜๋„๋ก ๋ง‰๋Š”๋‹ค.

๋ฌธ์„œํ™”(OpenAPI)

  • Authorization ํ—ค๋”๋Š” ๋ช…์‹œํ•˜๊ณ , userId ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” @Parameter(hidden = true)๋กœ ์ˆจ๊ธด๋‹ค.
    ๋ฌธ์„œ ์†Œ๋น„์ž๋Š” ํ† ํฐ๋งŒ ์ค€๋น„ํ•˜๋ฉด ๋˜๊ณ , ์ฃผ์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋…ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.

์ฃผ์˜์ 

  • ์ปจํŠธ๋กค๋Ÿฌ์—์„œ userId๋กœ ์กฐํšŒํ•œ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ ์‹œ ์ธ๊ฐ€(์†Œ์œ ๊ถŒ/๊ถŒํ•œ)๋ฅผ ์„œ๋น„์Šค ๋ ˆ๋ฒจ์—์„œ ์žฌ๊ฒ€์ฆํ•œ๋‹ค.

๐Ÿ“Œ ์š”์•ฝ: ํด๋ž˜์Šค๋ณ„ ์ฑ…์ž„ ํ•œ ์ค„ ์ •๋ฆฌ

  • @CurrentUserId: โ€œ์—ฌ๊ธฐ์—” ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž ID๊ฐ€ ๋“ค์–ด์˜จ๋‹คโ€๋Š” ์˜๋„ ์„ ์–ธ์„ ๋‹ด๋‹นํ•œ๋‹ค.
  • ๋ฆฌ์กธ๋ฒ„: ์„ ์–ธ๋œ ์˜๋„๋ฅผ ์‹ค์ œ ๊ฐ’์œผ๋กœ ์ฃผ์ž…ํ•˜๊ณ , ์‹คํŒจ๋ฅผ 401 ํ‘œ์ค€ํ™”ํ•œ๋‹ค.
  • WebMvcConfig: ๋ฆฌ์กธ๋ฒ„๋ฅผ ์Šคํ”„๋ง ์ƒํƒœ๊ณ„์— ์—ฐ๊ฒฐํ•œ๋‹ค.
  • ์ปจํŠธ๋กค๋Ÿฌ: ๋น„์ฆˆ๋‹ˆ์Šค ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋ฐ›์•„ ํ•ต์‹ฌ ๋กœ์ง์— ์ง‘์ค‘ํ•œ๋‹ค.


๐Ÿงญ 4. ๋™์ž‘ ํ๋ฆ„

[Client] --Bearer ํ† ํฐ-->
  [JWT ํ•„ํ„ฐ] --๊ฒ€์ฆ OK-->
    [SecurityContext(Authentication)]
      โ””โ”€> [ArgumentResolver] --@CurrentUserId ์ฃผ์ž…-->
           [Controller(Long userId)] โ†’ [Service] (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋งŒ)


๐Ÿš€ 5. ์ ์šฉ ํšจ๊ณผ

  • ์ปจํŠธ๋กค๋Ÿฌ ๋‹จ์ˆœํ™”: ์ธ์ฆ ํŒŒ์ƒ ๋กœ์ง ์ œ๊ฑฐ โ†’ ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ์ค‘์‹ฌ์œผ๋กœ ์ •๋ฆฌ๋œ๋‹ค.
  • ์ผ๊ด€๋œ ๋ณด์•ˆ/์˜ˆ์™ธ: ์ธ์ฆ ์‹คํŒจ๋Š” ํ•ญ์ƒ 401 UNAUTHORIZED๋กœ ํ†ต์ผํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๋น„์šฉ ์ ˆ๊ฐ: SecurityContextProvider.setupSecurityContextForTest(...)๋กœ ์ธ์ฆ ๋งฅ๋ฝ์„ ์†์‰ฝ๊ฒŒ ๊ตฌ์„ฑํ•œ๋‹ค.
  • ๋ฌธ์„œ ํ’ˆ์งˆ ํ–ฅ์ƒ: Swagger์—์„œ Authorization๋งŒ ์š”๊ตฌ, ๋‚ด๋ถ€ ์ฃผ์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์ˆจ๊ฒจ ์ŠคํŽ™์ด ๊ฐ„๊ฒฐํ•ด์ง„๋‹ค.
  • ํ™•์žฅ ์šฉ์ด์„ฑ: ๋งŒ์•ฝ @CurrentUserRole, @CurrentOrgId ๋“ฑ ๋ฆฌ์กธ๋ฒ„๋ฅผ ์ถ”๊ฐ€ํ•ด๋„ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์‹ค์ˆ˜ ์—ฌ์ง€ ์ถ•์†Œ: ์ง์ ‘ ํ—ค๋” ํŒŒ์‹ฑ/Principal ์บ์ŠคํŒ… ์ œ๊ฑฐ๋กœ ์˜ค๋‚จ์šฉ์„ ์ตœ์†Œํ™”ํ•œ๋‹ค.


๐Ÿ› ๏ธ 6. ํ™•์ธํ•˜๊ธฐ

1) JWT ํ•„ํ„ฐ ์ ๊ฒ€: ๊ฒ€์ฆ ์„ฑ๊ณต ์‹œ Authentication(principal=userId, role)์ด ์ •ํ™•ํžˆ ์ฑ„์›Œ์ง€๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
2) ๋ฆฌ์กธ๋ฒ„ ๋„์ž…: @CurrentUserId, ๋ฆฌ์กธ๋ฒ„, WebMvcConfig๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
3) ์ปจํŠธ๋กค๋Ÿฌ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๊ต์ฒด: @CurrentUserId Long userId๋กœ ๋ฐ”๊พธ๊ณ  ๋‚ด๋ถ€ ์ธ์ฆ ์ ‘๊ทผ ์ฝ”๋“œ๋ฅผ ์‚ญ์ œํ•œ๋‹ค.
4) ๋ฌธ์„œํ™” ์ •๋ฆฌ: Swagger์—์„œ Authorization ํ—ค๋”๋Š” ๋ช…์‹œํ•˜๊ณ , userId๋Š” ์ˆจ๊ธด๋‹ค.


โ“ 7. FAQ

  • ์ต๋ช… ํ—ˆ์šฉ API๋Š”? -> @CurrentUserId๋ฅผ ์“ฐ์ง€ ์•Š๊ณ , ํ•„์š” ์‹œ nullable ์ฃผ์ž…์šฉ ๋ณ„๋„ ๋ฆฌ์กธ๋ฒ„๋ฅผ ๋‘”๋‹ค.
  • Long ์™ธ ํƒ€์ž…์€?
    -> supportsParameter๋ฅผ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž ๊ฐ์ฒด ์ž์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ์• ๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
    -> ์—ฌ๊ธฐ์„  supportsParameter์— Long ์กฐ๊ฑด์„ ๊ฑธ์–ด๋‘์—ˆ๊ธฐ์— Long ํƒ€์ž…๋งŒ ๋ฐ›๋Š”๋‹ค.
  • ๋น„๋™๊ธฐ์—์„œ ์ปจํ…์ŠคํŠธ ์ „ํŒŒ๋Š”? -> ์Šคํ”„๋ง Security์˜ ์ปจํ…์ŠคํŠธ ์ „ํŒŒ ์„ค์ •์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ๋น„๋™๊ธฐ ๊ฒฝ๊ณ„์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ๋ณต์ œยท์ „๋‹ฌํ•ด์•ผํ•จ.

๐Ÿงช 8. ํ…Œ์ŠคํŠธ ํŒ

SecurityContextProvider.setupSecurityContextForTest(1L, RoleType.USER);
// ํ…Œ์ŠคํŠธ ...
SecurityContextProvider.clearSecurityContext();
  • ๊ถŒํ•œ๋ณ„ ์‹œ๋‚˜๋ฆฌ์˜ค(USER/ADMIN)์™€ ์‹คํŒจ ์ผ€์ด์Šค(๋ฏธ์ธ์ฆ/๋งŒ๋ฃŒ)๋ฅผ ๋ถ„๋ฆฌํ•ด ๊ฒ€์ฆํ•œ๋‹ค๋ฉด ์œ„์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.
  • ์ปจํŠธ๋กค๋Ÿฌ ํ…Œ์ŠคํŠธ๋Š” ์ฃผ์ž… ๋™์ž‘์— ์ง‘์ค‘ํ•˜๊ณ , JWT ๊ฒ€์ฆ ์ž์ฒด๋Š” ๋”ฐ๋กœ ํ…Œ์ŠคํŠธ๋กœ ์ปค๋ฒ„ํ•œ๋‹ค.

๐ŸŽฏ 9. ๊ฒฐ๋ก 

์ปจํŠธ๋กค๋Ÿฌ์—๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋‚จ๊ธด๋‹ค. ์œ ์ € ID๋Š” ๋ฆฌ์กธ๋ฒ„๊ฐ€ ์ผ๊ด€๋˜๊ฒŒ ์ฃผ์ž…ํ•œ๋‹ค.
์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ”๋“œ๋Š” ์งง์•„์ง€๊ณ , ๋ณด์•ˆ/์˜ˆ์™ธ๋Š” ํ†ต์ผ๋˜๋ฉฐ, ํ…Œ์ŠคํŠธ์™€ ๋ฌธ์„œํ™”๊ฐ€ ์‰ฌ์›Œ์ง„๋‹ค. ๐Ÿš€

profile
๋งค์ผ ๋งค์ผ ์„ฑ์žฅํ•˜๊ธฐ

0๊ฐœ์˜ ๋Œ“๊ธ€