Learned/Spring

    Spring Boot ์™ธ๋ถ€ API ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ๋ณด์ž! [MockWebServer vs WireMock]

    โ“ Mock ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ  Spring Boot Github ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๊ธฐ [ RestTemplate · WebClient · FeignClient ๋ฅผ ๋น„๊ตํ•ด๋ณด์ž ] Github OAuth ์ธ์ฆ ํ๋ฆ„๊ณผ ์‚ฌ์ „ ์ค€๋น„ Github ์ธ์ฆ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 1. https://github.com/login/oauth/authorize?client_id={๋ฐœ๊ธ‰๋ฐ›์€ client_id} ๋กœ ์ด๋™ํ•˜๋Š” ๋งํฌ๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญํ•œ๋‹ค. 2. ์‚ฌ์šฉ์ž๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ํ™” yinq.tistory.com REST API ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” RestTemplate · WebClient · FeignClient ํด๋ž˜์Šค๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์™ธ๋ถ€ API ์š”์ฒญ์„ ํ†ตํ•ด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ์—ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ณด๋‹ˆ, ..

    Spring Boot Github ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๊ธฐ [ RestTemplate · WebClient · FeignClient ๋ฅผ ๋น„๊ตํ•ด๋ณด์ž ]

    Github OAuth ์ธ์ฆ ํ๋ฆ„๊ณผ ์‚ฌ์ „ ์ค€๋น„ Github ์ธ์ฆ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 1. https://github.com/login/oauth/authorize?client_id={๋ฐœ๊ธ‰๋ฐ›์€ client_id} ๋กœ ์ด๋™ํ•˜๋Š” ๋งํฌ๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญํ•œ๋‹ค. 2. ์‚ฌ์šฉ์ž๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚˜๋Š” ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•˜๊ณ , Github ์ธ์ฆ์„ ํ•œ๋‹ค. 3. ์ธ์ฆ์„ ์„ฑ๊ณตํ•˜๋ฉด {์„ค์ •ํ•œ ์ฝœ๋ฐฑ URL}?code={์ธ์ฆ์ฝ”๋“œ} ๋กœ code ๊ฐ’์„ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ํ˜•ํƒœ๋กœ ๋ณด๋‚ด์ค€๋‹ค. 4. ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ๋œ code๋ฅผ ์„œ๋ฒ„์—์„œ ๋ฐ›๊ณ , https://github.com/login/oauth/access_token ์œผ๋กœ {client_ID, client_Secret, code}๋ฅผ ๋‹ด์•„ POST ์š”์ฒญ์„ ํ•œ๋‹ค. 5. ๊นƒํ—ˆ๋ธŒ ์ธก์—์„œ acc..

    Dispatcher Servlet์€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ๊นŒ?

    DispatcherServlet ์€ Servlet ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ, HTTP ํ”„๋กœํ† ์ฝœ๋กœ ๋“ค์–ด์˜ค๋Š” ๋ชจ๋“  ์š”์ฒญ์„ ๋จผ์ € ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š” Front Controller ์ด๋‹ค. Servlet ์ด๋ž€? Java ์–ธ์–ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ด๋Š” HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‘๋‹ตํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์ด๋‹ค. public interface Servlet { void init(ServletConfig config) throws ServletException; ServletConfig getServletConfig(); void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; String getServlet..

    Swagger API ์ ์šฉ ์‹œ, Controller ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋”๋Ÿฌ์›Œ์ง„๋‹ค.. ๋ถ„๋ฆฌํ•ด๋ณด์ž

    Swagger ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, API ๋ฌธ์„œ๋ฅผ ์ž๋™์œผ๋กœ ๊ตฌ์„ฑํ•ด์ฃผ๋ฉฐ ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค. @Tag , @Operation , @ApiResponses ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์Šค์›จ๊ฑฐ ๋ฌธ์„œ์— ๋ถ€๊ฐ€์ ์ธ ์„ค๋ช…์„ ์ถ”๊ฐ€์ ์œผ๋กœ ๋„ฃ์„ ์ˆ˜ ์žˆ์–ด์„œ API ๋ฌธ์„œ๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ ๋„์›€์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค. @RestController @RequestMapping("/api/v1/posts") @RequiredArgsConstructor public class PostApiController { private final PostService postService; @Tag(name = "Post", description = "๊ฒŒ์‹œ๊ธ€ ๊ด€๋ จ API") @Operation(summary = "๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ", descrip..

    Dto Validation ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์— AOP๋ฅผ ์ ์šฉํ•ด๋ณด์ž!

    ๊ธฐ์กด ๋ฐฉ์‹ implementation 'org.springframework.boot:spring-boot-starter-validation' ์œ„ validation ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Controller ์—์„œ @RequestBody ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋งคํ•‘ํ•˜๋Š” Request Dto ์˜ ํ•„๋“œ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ๋‹ค. public class UserCreateRequest { @Email(message = "์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค. '@' ๋ฅผ ํฌํ•จ์‹œ์ผœ์ฃผ์„ธ์š”.") private String email; @Length(min = 8,message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ตœ์†Œ 8์ž ์ด์ƒ์ž…๋‹ˆ๋‹ค.") private String password; } ์œ„์™€ ๊ฐ™์ด ๋งคํ•‘๋˜๋Š” ํ•„๋“œ์— validation์—์„œ ์ œ๊ณตํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์„..

    ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€ ๋ถ„์„ ๋„๊ตฌ Jacoco๋ฅผ ์ ์šฉํ•ด๋ณด์ž!

    Jacoco ๋ž€? Java Code Coverage ์˜ ์ค„์ž„๋ง๋กœ, ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€ ๋ถ„์„์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. โ“ ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€๋ž€? ์†Œํ”„ํŠธ์›จ์–ด์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ถฉ์กฑ๋˜์—ˆ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ง€ํ‘œ ์ค‘ ํ•˜๋‚˜์ด๋‹ค. 1. ๋ผ์ธ ์ปค๋ฒ„๋ฆฌ์ง€ : ์ฝ”๋“œ ํ•œ ์ค„์ด ํ•œ ๋ฒˆ ์ด์ƒ ์‹คํ–‰๋œ๋‹ค๋ฉด ์ถฉ์กฑ๋œ๋‹ค. 2. ๊ฒฐ์ • ์ปค๋ฒ„๋ฆฌ์ง€ : ๋ธŒ๋žœ์น˜ ์ปค๋ฒ„๋ฆฌ์ง€๋ผ๊ณ ๋„ ๋ถˆ๋ฆฐ๋‹ค. ๋ชจ๋“  ์กฐ๊ฑด์‹์˜ ๋‚ด๋ถ€ ์กฐ๊ฑด์ด true/ false ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋˜๋ฉด ์ถฉ์กฑํ•œ๋‹ค. 3. ์กฐ๊ฑด ์ปค๋ฒ„๋ฆฌ์ง€ : if ์กฐ๊ฑด๋ฌธ ์•ˆ์˜ ๊ฐœ๋ณ„ ์กฐ๊ฑด์‹์ด false ์ธ๊ฒฝ์šฐ์™€ true์ธ ๊ฒฝ์šฐ ๋ชจ๋‘ ์‹คํ–‰๋˜์—ˆ์„ ๋•Œ ์ถฉ์กฑํ•œ๋‹ค. ๊ฒฐ์ • ์ปค๋ฒ„๋ฆฌ์ง€๋ณด๋‹ค ๋” detailํ•œ ์ปค๋ฒ„๋ฆฌ์ง€์ด๋‹ค. ์ ์šฉํ•ด๋ณด์ž ์ ์šฉ ์ „ build.gradle plugins { id 'java' id 'org.springframework.boot'..

    @ControllerAdvice ๋Š” ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ผ๊นŒ?

    ๐Ÿ“Œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐฉ์‹ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ, ๋‚ด๊ฐ€ ์ฃผ๋กœ ํ–ˆ๋˜ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. Enum ํƒ€์ž…์˜ ErrorCode ์ •์˜ @AllArgsConstructor @Getter public enum ErrorCode { DUPLICATE_USERNAME(HttpStatus.CONFLICT, "์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์‚ฌ์šฉ์ž๋ช… ์ž…๋‹ˆ๋‹ค."); private HttpStatus httpStatus; private String message; } Http ์ƒํƒœ ์ฝ”๋“œ์™€ ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์ „๋‹ฌํ•  ๋ฉ”์„ธ์ง€ ์ •๋ณด๊ฐ€ ๋‹ด๊ธด Enum ํƒ€์ž…์˜ ErrorCode ๋ฅผ ์ •์˜ํ•œ๋‹ค. RuntimeException ์„ ์ƒ์†ํ•˜๋Š” AppException์„ ์ •์˜ @Getter public class AppException extends RuntimeExc..