๐Ÿ‘๊ฒช์€ ๋ฌธ์ œ - kkminseok/real-world-springboot Wiki

9.7 Prefix์„ค์ •, ํ†ฐ์บฃ ์ค‘๋ณต์œผ๋กœ ๋– ์žˆ์„๋•Œ ํ•ด๊ฒฐ
  • FastAPI์ฒ˜๋Ÿผ router prefix์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ธฐ.

WebConfig์œผ๋กœ ํ•˜๋Š”๊ฒŒ ์žˆ์œผ๋‚˜ 2.x๋ฒ„์ „์—์„œ ์ง€์›์•ˆํ•œ๋‹ค๋Š” ๊ธ€์„๋ด„. application.properties์—์„œ server.servlet.contextPath๋กœ ์„ค์ •. ์•Œ์•„๋ณด๊ณ  ๋ธ”๋กœ๊ทธ์— ๊ธ€ ์“ธ๊ฒƒ. ๊ณต์‹๋ฌธ์„œ

  • ํ†ฐ์บฃ ์ด๋ฏธ ๋–  ์žˆ์„๊ฒฝ์šฐ ๊ฐ•์ œ ์ข…๋ฃŒ๋ฒ•
# window
netstat -ano > ํ”„๋กœ์„ธ์Šค idํ™•์ธ
taskkill /f /pid ํ”„๋กœ์„ธ์Šคid > ๊ฐ•์ œ๋กœ pid๋ฒˆํ˜ธ ์ข…๋ฃŒ
9.8 ๋ณต์žกํ•œ collections์„ JSON์œผ๋กœ ๋ฐ›์œผ๋ ค๋ฉด

@JsonTypeName() @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)

๊ตฌ๋ฌธ์— ๋Œ€ํ•ด์„œ ์•Œ ํ•„์š”๊ฐ€ ์žˆ์Œ. listํ˜•ํƒœ์˜ ๊ฐ์ฒด๋ฅผ ์ฃผ๊ณ ๋ฐ›์„๋•Œ HashMap์„ ์•ˆ์“ฐ๊ณ  ๋ฐ›์œผ๋ ค๋ฉด ์œ„์ฒ˜๋Ÿผ ๋ช…์‹œ๋ฅผ ํ•ด์ค˜์•ผํ•จ.

EX)

{
  "user": {
    "email": "[email protected]",
    "password": "<password>",
    "username": "<string>"
  }
}

์ด๋Ÿฐ์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„๋•Œ๋‹ค.

9.9 lombok ์ ์šฉ ์•ˆ๋จ, mockMVCํ…Œ์ŠคํŠธ ๊ฒ€์ฆ ๋ฐฉ์‹์— ๋Œ€ํ•˜์—ฌ , ํ…Œ์ŠคํŠธ์ฝ”๋“œ ์ธ์ฆ๋ฐฉ์‹๊ด€๋ จ
  • lombok ์ ์šฉ์ด ์•ˆ๋จ

build.grale์—

annotationProcessor 'org.projectlombok:lombok'

์ถ”๊ฐ€

์ถ”๊ฐ€

  • Test Code๋ฅผ ์งœ๋Š”๋ฐ, ๋ฆฌ์ŠคํŠธ๋กœ ๋ณด๋‚ด๋Š”๋ฐ์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊น€. ๊ฐ์ฒด ์ž์ฒด๋ฅผ ๋ณด๋‚ด๋Š”๊ฑด ๊ดœ์ฐฎ์€๋ฐ, user: {"username" : "kms"} ์ด๋Ÿฐ์‹์œผ๋กœ ๋ณด๋‚ด๋Š”๊ฒƒ์„ ํŒŒ์‹ฑํ•˜๋Š”๋ฐ ์–ด๋ ค์›€์„ ๊ฒช์Œ.

$.user.image ๊ฐ™์€ ๋ฌธ๋ฒ•์„ ๊ณต๋ถ€ํ•˜๋ฉด ๋จ.

.andExpect(jsonPath("$.user.email", signupResponseTest.getEmail()).exists())
9.10 WebSecurityConfigurerAdapter `Deprecated`๋˜์–ด๋ฒ„๋ฆผ.
  1. WebSecurityConfigurerAdapter๊ฐ€ deprecated๋˜์–ด๋ฒ„๋ ค์„œ ์ด๋ฅผ ํ•ด๊ฒฐํ•ด์•ผํ•จ. ์„ค์ • ๋ถ€๋ถ„์ด ์•„์˜ˆ ๋ฐ”๋€Œ๊ธฐ ๋•Œ๋ฌธ์— ๋งž๋Š” ๋ฐฉ์‹์„ ์ฐพ์•„์„œ ๋‹ค์‹œ ์ ์šฉ์‹œ์ผœ์ค˜์•ผํ•˜๋Š” ๋ฌธ์ œ.
Failed to load resource: the server responded with a status of 403 ()

๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ.

์ตœ๊ทผ ๋ฒ„์ „์ด ๋ฐ”๋€Œ๋ฉด์„œ ์ƒ๊ธด ๋ฌธ์ œ๋ผ ๊ตฌ๊ธ€๋งํ•ด๋„ ์ž˜ ์•ˆ๋‚˜์˜ด ๋„ํ ์ฝ๋Š”์ค‘

๋ฒ„์ „์„ ๋‚ฎ์ถฐ์„œ ํ•ด๊ฒฐํ•  ์ˆœ ์žˆ๊ฒ ์ง€๋งŒ ํ•˜๋‚จ์ž๋ฐฉ์‹์„ ํƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

.antMatchers("/api/users/**").permitAll()
#์—์„œ 
.antMatchers("/users/**").permitAll()
#๋กœ ์ˆ˜์ •

์ฒ˜๋Ÿผ ์š”์ฒญ ๋ฒ”์œ„๋ฅผ ํ—ˆ์šฉ ํ•  ์ˆ˜ ์žˆ์—ˆ๋Š”๋ฐ url์„ ๊ณ„์† ์ž˜๋ชป์„ค์ •ํ•ด์คฌ๋‹ค. application.properties์—์„œ server.servlet.contextPath=/api๋กœ ์„ค์ •ํ•ด๋‘”๊ฒƒ์ด ์šฐ์„ ์ฒ˜๋ฆฌ ๋˜๋Š”์ง€๋ฅผ ๋ชฐ๋ž์Œ.

  1. Test Code ์ด์Šˆ

ํ…Œ์ŠคํŠธ์ฝ”๋“œ์—์„œ 403 error, 401error๋ฅผ ๋ฐ˜ํ™˜ํ•จ.

403error๋Š”

mockMvc.perform(
                post("/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(signupRequestTest))
                        .with(csrf())

์ฒ˜๋Ÿผ .with(csrf())๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค. ํ† ํฐ์ด ์—†์–ด์„œ ๋‚˜์˜จ ์—๋Ÿฌ์˜€๋‹ค.

401 error๋Š” ๊ถŒํ•œ์—๋Ÿฌ์ธ๋ฐ, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— @WithMockUser์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

@Test
@WithMockUser
...
9.20 ํ…Œ์ŠคํŠธ์ฝ”๋“œ ๋กœ์ง์— ๋Œ€ํ•œ ์ดํ•ด๋ถ€์กฑ, @AuthenticationPrincipal์ด ๋ถ™์€ ์ฝ”๋“œ ํ…Œ์ŠคํŠธํ•˜๋Š”๋ฒ•

ํ…Œ์ŠคํŠธ์ฝ”๋“œ ์ด์Šˆ๋ฐœ์ƒ.

๋กœ์ง์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ๋ถ€์กฑํ•˜์—ฌ ํ•˜๋ฃจ๋ฅผ ๊ณ ์ƒ.

๋กœ์ง ๋ณ€๊ฒฝ์œผ๋กœ ํ•ด๊ฒฐ์™„๋ฃŒ.

when(userRepository.findByEmail(any(String.class))).thenReturn(null).thenThrow(new CustomException(Error.DUPLICATE_USER));
when(userRepository.save(any(User.class))).thenReturn(user);
userService.signup(requestUser);

๊ธฐ์กด์—๋Š” findByEmail()ํ•จ์ˆ˜๊ฐ€ ๊ณ„์† User๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์˜ค๋ฅ˜๊ฐ€๋‚จ. ์ฒ˜์Œ์—๋Š” null์„ ๋ฐ˜ํ™˜ํ•ด์•ผ save()๊นŒ์ง€ ๋„๋‹ฌํ•˜๋Š”๋ฐ null์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์•„์„œ ๊ณ„์† ์ค‘๋ณต ์˜ˆ์™ธ์ฒ˜๋ฆฌ ๋ฐœ์ƒ.

์ถ”๊ฐ€์ ์œผ๋กœ doThen -> when - then์œผ๋กœ ๋ฐ”๊ฟจ๋Š”๋ฐ ์ด์œ ๋ฅผ ์“ฐ๋ฉด ๋ธ”๋กœ๊ทธ์— ์ž‘์„ฑ


getCurrent() ์ปจํŠธ๋กค๋Ÿฌ ํ…Œ์ŠคํŠธ ์ค‘ ๋ฌธ์ œ ๋ฐœ์ƒ. ํ•„ํ„ฐ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  200OK๊ฐ€ ๋–จ์–ด์ง€๋ฉด์„œ body์— ์•„๋ฌด๊ฒƒ๋„ ๋‹ด์•„์˜ค์ง€ ์•Š์Œ.

UserResponse userResponse = UserResponse.builder()
                .username(userAuth.getUsername())
                .email(userAuth.getEmail())
                .image(userAuth.getImage())
                .bio(userAuth.getBio()).build();
        when(userService.getCurrentUser(any(UserAuth.class))).thenReturn(userResponse);
        mockMvc.perform(get("/api/user")
                        .with(csrf())
                ).andExpect(status().isOk())
                .andExpect(jsonPath("$.user.email", Matchers.equalTo(userResponse.getEmail())))

์˜ค๋ฅ˜๋‚ด์šฉ image

ํ•ด๊ฒฐ๋ฐฉ์•ˆ

when(userService.getCurrentUser(any(UserAuth.class))).thenReturn(userResponse);

์ด ์ฝ”๋“œ๋Š” UserAuthํด๋ž˜์Šค๋ฅผ ์š”๊ตฌํ•˜๊ณ  ์žˆ์œผ๋‚˜ ์‹ค์ œ ๊ฐ’์„ ๋„˜๊ธธ๋•Œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋„˜๊ธฐ์ง€ ์•Š์Œ.

๊ทผ๋ฐ real-world ๋‹ค๋ฅธ ์ž‘์„ฑ์ž ์ฝ”๋“œ์—์„œ๋Š” ์‹ค์ œ๋กœ ๊ฐ’์„ ์•„๋ฌด๊ฒƒ๋„ ๋„˜๊ธฐ์ง€ ์•Š์•„๋„ ํ…Œ์ŠคํŠธ๊ฐ€ ์„ฑ๊ณตํ•˜๋‚˜๋ด„. ๋ถ„์„ํ•„์š”

@WithSecurityContext(factory = WithAuthUserSecurityContextFactory.class) ๋ฅผ ๋ถ„์„. @AuthenticationPrincipal๊ฐ€ ๋ถ™์–ด์žˆ๋Š”๊ฑฐ ํ…Œ์ŠคํŠธํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค๊ณ ํ•จ. ๋ธ”๋กœ๊ทธ์— ์ •๋ฆฌ ์ฐธ๊ณ , ๊ณต์‹๋ฌธ์„œ

ํ•ด๊ฒฐ : ๊ทธ๋ƒฅ ๊ณต์‹๋ฌธ์„œ ๋ณด๋ฉด์„œ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๋‹ˆ ๋˜์—ˆ์Œ. ๋ธ”๋กœ๊ทธ์— ๊ธฐ๋กํ•  ์˜ˆ์ •

9.21 JSON ๊ฐ์ฒด deserialize ๋ฌธ์ œ

๋ฐœ์ƒ ํ™˜๊ฒฝ : ์œ„์˜ ์ธ์ฆ๋ฐฉ์‹ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๋‹ˆ ๋ฐœ์ƒํ•˜์˜€์Œ.

๋ฐœ์ƒ :

image

image

ํ•ด๊ฒฐ๋ฒ• : ํ•ด๋‹น ๊ฐ์ฒด์— @NoArgsConstructor๋ฅผ ๋ถ™์ด๋ฉด ๋˜๊ธดํ•˜์ง€๋งŒ..์ด๋Š” ๊ทผ๋ณธ์ ์ธ ํ•ด๊ฒฐ์ฑ…์ด ์•„๋‹ˆ๊ธฐ์— ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ฐพ์Œ.

์ด์œ  : ์œ„์˜ ์ธ์ฆ๋ฐฉ์‹์„ ์ˆ˜ํ–‰ํ•˜๋ฉด์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ชฉ๋ก์„ ๋ณด๊ธฐ ์œ„ํ•ด intellij ์„ค์ •์„ ๋ฐ”๊พผ๊ฒŒ ์žˆ์Œ.

image

์ด ๋ถ€๋ถ„์ธ๋ฐ, Build and run using์ด ๋ถ€๋ถ„์„ intellij๋กœ ๋ฐ”๊พธ๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ. ๋‹ค์‹œ gradle๋กœ ๋ฐ”๊ฟ”์„œ ์„ฑ๊ณต.

image

intellij build์™€ gradle build๋ฅผ ๋น„๊ตํ•ด์„œ ๋ธ”๋กœ๊ทธ์— ์˜ฌ๋ ค์•ผ์ง€.

9.23 updateํ•ด๋„ ๊ฐ’์ด ๋ฐ”๋€Œ์ง€ ์•Š์Œ.
public UserResponse updateUser(UserUpdate userUpdate, UserAuth userAuth){
        User user = userRepository.findById(userAuth.getId()).orElseThrow(() -> new CustomException(Error.USER_NOT_FOUND));
        if(userUpdate.getUsername() != null){
            userRepository.findByUsername(userUpdate.getUsername())
                    .filter(found -> !found.getId().equals(userRepository.findById(user.getId())))
                    .ifPresent(found -> new CustomException(Error.DUPLICATE_USER));
            System.out.println(user.getUsername() + userUpdate.getUsername());
            user.changeUsername(userUpdate.getUsername());
            System.out.println("!!" + user.getUsername() );
        }

        if(userUpdate.getEmail() != null){
            userRepository.findAllByEmail(userUpdate.getEmail())
                    .stream().filter(found -> !found.getId().equals(userRepository.findById(user.getId())))
                        .findAny().ifPresent(found -> new CustomException(Error.DUPLICATE_USER));
            user.changeEmail(userUpdate.getEmail());
        }

        user.update(userUpdate);

        return convertUser(userRepository.save(user));
    }

์ด๋ ‡๊ฒŒ ์ ์–ด๋„ ๋ฐ”๋€Œ์ง€ ์•Š์Œ. ๋ฌด์—‡์ด ๋ฌธ์ œ์ธ์ง€ ์ƒ๊ฐํ•ด์•ผํ•จ.

@Tranactional ์“ฐ๋‹ˆ๊นŒ ํ•ด๊ฒฐ์€ ๋จ.

๊ทธ๋Ÿผ ์›์ธ์„ ์ฐพ์•„์•ผํ•œ๋‹ค.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ