- ๋ชจ๋ API์ ์๋ต์ด ํต์ผ๋์ง ์์ผ๋ฉด ํ๋ก ํธ์ ์ ์ฅ์์ ์ฌ์ฉํ๊ธฐ ํ๋ค๋ค.
- ๋ฐ๋ผ์ ํ๋ก์ ํธ๋ง๋ค API๋ฅผ ํต์ผํ์ฌ์ผ ํ๋ค.
- ๋ณดํต์ ๊ฒฝ์ฐ ์๋ 4๊ฐ์ง์ ์ ๋ณด๋ฅผ ํฌํจํ๋๋ก ์์ฑํ๋ค.
- isSuccess : Boolean ํ์ ์ ์ฑ๊ณต ์ฌ๋ถ
- code : HTTP ์ํ ์ฝ๋ ์ธ์ ๋ ์ธ๋ถ์ ์ธ ๊ฒฐ๊ณผ
- message : code์ ์ถ๊ฐ์ ์ผ๋ก ์ด๋ค ๊ฒฐ๊ณผ์ธ์ง๋ฅผ ์๋ ค์ฃผ๊ธฐ ์ํด ์ฌ์ฉ
- result : ์๋ต์ผ๋ก ํ์ํ ๋ ๋ค๋ฅธ json ์ ๋ณด
- ํ๋ก์ ํธ ํ์ผ์์ ๊ตฌํ ์ ์๋์ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์ด๋ (I)๋ ์ธํฐํ์ด์ค (E)๋ ์ด๋ ํด๋์ค๋ฅผ ์๋ฏธํ๋ค.
๐com.example.umc9th
โโ ๐domain
โ โโ ๐member
โ โโ ๐controller
โ โโ ๐converter
โ โโ ๐dto
โ โ โโ ๐req
โ โ โ โโ MemberReqDto
โ โ โโ ๐res
โ โ โโ MemberResDto
โ โโ ๐service
โ โโ ๐command
โ โโ ๐query
โโ ๐global
โโ ๐apiPayload
โโ ๐code
โ โโ (I) BaseErrorCode
โ โโ (I) BaseSuccessCode
โ โโ (E) GeneralErrorCode
โ โโ (E) GeneralSuccessCode
โโ ApiResponse
- ์ถ๊ฐ์ ์ผ๋ก ์๋น์ค๋ฅผ ์์ฑํ ๋๋ ์๋ ๋ ๊ฐ์ง ํ์ผ์ ๋๋ ์์ฑํ๋ค.
- Query : GET ์์ฒญ์ ๋ํ ๋น์ฆ๋์ค ๋ก์ง๋ค
- Command : ์ด์ธ ์์ฒญ์ ๋ํ ๋น์ฆ๋์ค ๋ก์ง๋ค
code๋ ์๋ต ์ฝ๋๋ฅผ ๋ด๋ ์ญํ ์ ์ํํ๋ค. ํนํ ๋ฒ ์ด์ค ์ธํฐํ์ด์ค์์๋ ์ต์ํ์ ๊ตฌํ ๋ฉ์๋๋ฅผ ์ ํ๋ค.
public interface BaseErrorCode {
HttpStatus getStatus();
String getCode();
String getMessage();
}๋ฒ ์ด์ค ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ์ด๋ ๊ตฌํ ํด๋์ค์์๋ ์ํ ์ฝ์ค, ์์ธ ์ฝ๋, ๋ฉ์์ง๋ฅผ ์ ํ๋ค.
@Getter // ๋กฌ๋ณต์ ํตํด ์ธํฐํ์ด์ค์์ ์ ์ํ ๊ฒํฐ๋ค์ ์ค์ ๋ก ๊ตฌํํจ
@AllArgsConstructor
public enum GeneralErrorCode implements BaseErrorCode{
BAD_REQUEST(HttpStatus.BAD_REQUEST,
"COMMON400_1",
"์๋ชป๋ ์์ฒญ์
๋๋ค."),
UNAUTHORIZED(HttpStatus.UNAUTHORIZED,
"AUTH401_1",
"์ธ์ฆ์ด ํ์ํฉ๋๋ค."),
FORBIDDEN(HttpStatus.FORBIDDEN,
"AUTH403_1",
"์์ฒญ์ด ๊ฑฐ๋ถ๋์์ต๋๋ค."),
NOT_FOUND(HttpStatus.NOT_FOUND,
"COMMON404_1",
"์์ฒญํ ๋ฆฌ์์ค๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."),
;
private final HttpStatus status;
private final String code;
private final String message;
}- ์๋ต์ ์ํ ๊ณตํต API ํด๋์ค๋ ์๋์ ๊ฐ์ด ์์ฑํ ์ ์๋ค.
- ์ด๋ result์ ์ด๋ค ๊ฐ์ด ๋ด๊ธฐ๊ฒ ๋ ์ง ์ ์ ์์ผ๋ฏ๋ก ์ ๋ค๋ฆญ ํ์ ์ผ๋ก ์์ฑํ๋ค.
@Getter
@AllArgsConstructor
@JsonPropertyOrder({"isSuccess", "code", "message", "result"})
public class ApiResponse<T> {
@JsonProperty("isSuccess")
private final Boolean isSuccess;
@JsonProperty("code")
private final String code;
@JsonProperty("message")
private final String message;
@JsonProperty("result")
private T result;
// ์ฑ๊ณตํ ๊ฒฝ์ฐ (๊ฒฐ๊ณผ ํฌํจ)
public static <T> ApiResponse<T> onSuccess(BaseSuccessCode code, T result) {
return new ApiResponse<>(true, code.getCode(), code.getMessage(), result);
}
// ์คํจํ ๊ฒฝ์ฐ (๊ฒฐ๊ณผ ํฌํจ)
public static <T> ApiResponse<T> onFailure(BaseErrorCode code, T result) {
return new ApiResponse<>(false, code.getCode(), code.getMessage(), result);
}
}ํด๋น ๊ณผ์ ๋ค์ ๋๋ด๋ฉด API ์๋ต์ ์ํ ์ต์ํ์ ํต์ผ์ด ๋ง๋ฌด๋ฆฌ ๋๋ค.
- ์๋ต API์ ๊ฒฐ๊ณผ๋ก ๋ด๊ธฐ ์ํ ์ ๋ณด๋ค๊ณผ ํ๋ก ํธ์์ ์ ์ก๋ฐ๋ ์ ๋ณด๋ค์ ํฌ์ฅํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ DTO๋ค์ ์ ์ํ๋ค.
- DTO๋ค์ MemberResDto, ReviewResDto ๋ฑ๋ฑ ํฐ ์นดํ ๊ณ ๋ฆฌ์์ public ํด๋์ค๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์์ ์ธ๋ถ์ ์ผ๋ก static ํด๋์ค๋ฅผ ์ ์ํด ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
- DTO๋ ์์ฃผ ๋ง์ ๊ณณ์์ ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ ๋งค๋ฒ ํด๋์ค๋ฅผ ์์ฑํ๋ ๊ฒ๋ณด๋ค ํ๋์ ํด๋์ค ๋ด์์ static ํด๋์ค๋ฅผ ์์ฑํ๋ ๊ฒ์ด ๋งค์ฐ ํจ์จ์ ์ด๋ค.
- ๋ํ MemberResDto ํด๋์ค ์์์ ์ ์ธ๋๋ ๊ฒ์ด๋ฏ๋ก ๋ชจ๋ DTO์ ์ผ์ผ์ด Member๋ผ๋ ๋๋ฉ์ธ์ ์ด๋ฆ์ ์ ๋ ฅํด์ค ํ์๊ฐ ์๋ค.
- ์ฌ์ฉ ์์๋ MemberResDto.changeNickname์ฒ๋ผ ์ธ๋ถ ํด๋์ค ์ด๋ฆ๊ณผ ๋ด๋ถ ํด๋์ค ์ด๋ฆ์ ํจ๊ป ์ ์ด ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
public class MemberResDto { // ํฐ ๋ฌถ์์ผ๋ก ํด๋์ค ์์ฑ
@Builder
@Getter
public static class ChangeNickname { // ๋ด๋ถ์์ public static์ผ๋ก ์ ์ธํ ๋ค ์ฌ์ฉ
private String nickname;
}
}- ์๋ต DTO์ ๊ฒฝ์ฐ, ๋ฐฑ์ค๋ ์ธก์์ ๋ด์ฉ์ ์ฑ์์ผ ํ๋ฏ๋ก ๋นํฐ ํจํด์ ์ฌ์ฉํ๋ค.
- ์์ฒญ DTO์ ๊ฒฝ์ฐ, ํ๋ก ํธ์์ ์ธก์์ ๋ง๋ค์ด์ง ๊ฐ์ฒด์ ์ ๋ณด๋ฅผ ํ ๋ฒ์ ๋ฐ์์ค๋ ๊ฒ์ด๋ฏ๋ก ๋น๋ ํจํด์ ๊ฑฐ์ ์ฌ์ฉํ์ง ์๋๋ค.
- ๊ฐ์ฒด๋ฅผ DTO๋ก ๋ฐ๊พธ๋ ํด๋์ค.
- ์ฌ๋ฌ ๊ฐ์ฒด์ ํ์ํ ์ ๋ณด๋ฅผ ๋ฐ์ ํ์ํ ์ ๋ณด๋ง์ ๊ฑธ๋ฌ DTO๋ก ํฌ์ฅํด์ค๋ค.
public class MemberConverter {
// ๋ฉค๋ฒ ๊ฐ์ฒด๋ฅผ ๋ฐ์ ๊ด๋ จ DTO๋ก ๋ณํํด์ค
public static MemberResDto.ChangeNickname toNicknameDTO(Member member) {
return MemberResDto.ChangeNickname.builder()
.nickname(member.getNickname())
.build();
}
}@Controller: ์ ํต์ ์ธ MVC์์ View(ํ๋ฉด, HTML)๋ฅผ ๋ฐํํ๊ธฐ ์ํด ์ฌ์ฉ@RestController- RESTful API๋ฅผ ๊ตฌ์ถํ ๋ ์ฌ์ฉํ๋ค.
@Controller+@ResponseBody๊ฐ ํฉ์ณ์ง ์ด๋ ธํ ์ด์ ์ด๋ค.- ๋ฐ์ดํฐ(JSON)๋ฅผ ๋ฐํํ๋ ๊ฒ์ด ์ฃผ ๋ชฉ์ ์ด๋ค.
@RestController
@RequiredArgsConstructor
@RequestMapping("/members") // ์ปจํธ๋กค๋ฌ ์ ์ฒด ๊ฒฝ๋ก ์ค์
public class TestController {
@PatchMapping("/me")
public ApiResponse<MemberResDto.ChangeNickname> changeNickname(
@RequestBody @Valid MemberReqDto.ChangeNickname req) throws Exception {
/**
* ์๋น์ค ํด๋์ค์์ ๋๋ค์ ๋ณ๊ฒฝ ๋ก์ง์ด ์ด๋ฃจ์ด์ง
* MemberConverter.toNicknameDTO(member)๋ฅผ ๋ฐํํ๊ฒ ๋จ!!
*/
MemberResDto.ChangeNickname res = memberService.changeNickname(req); // ์๋น์ค ํด๋์ค ์คํ ํ DTO ๋ฐํ
GeneralSuccessCode code = GeneralSuccessCode.OK; // ์๋ต ์ฝ๋ ์ ์
return ApiResponse.onSuccess(
code,
res
);
}
}์ต์ข ์ ์ผ๋ก ํ๋ก ํธ์์ PATCH /members/me ๊ฒฝ๋ก๋ก ์์ฒญ์ ๋ณด๋ด๋ฉด ์ฑ๊ณต ๋ฉ์์ง์ ํจ๊ป ๋ณ๊ฒฝ๋ ๋๋ค์์ด ๋์์ค๊ฒ ๋๋ค.
- ์ค๋ฌด์์๋ HTTP ์๋ต ์์ฒด๋ฅผ ์ ์ดํ๋(HTTP ์ํ ์ฝ๋, ํค๋ ๋ฑ) ResponseEntity๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
- ๊ฐ์ฅ ๋ณดํธ์ ์ธ ๋ฐํ ํ์ ์ ResponseEntity<ApiResponse>์ด๋ค.
- ์ฆ,
EntityโDTOโApiResponseโResponseEntity๋ก ์ด์ด์ง๋ ํ๋ฆ์ด ๋๋ค! - ๊ท์ฐฎ์ ๋ฏ ๋ณด์ฌ๋ ์ด ๋ชจ๋ ๊ณผ์ ์ ๊ฑฐ์ณ์ผ ์์ธก ๊ฐ๋ฅํ๊ณ ์์ ์ ์ธ API๋ฅผ ๋ง๋ค ์ ์๋ค.
- HTTP ์ํ ์ฝ๋(200, 400 ๋ฑ) : ๊ธฐ๊ณ/์ธํ๋ผ๋ฅผ ์ํ ๊ฒ์ด๋ค. ๋ธ๋ผ์ฐ์ , ์บ์, ๋ชจ๋ํฐ๋ง ํด, ๋คํธ์ํฌ ์ฅ๋น ๋ฑ์ด ์ฌ์ฉํ๋ค.
- ApiResponse ๋ด๋ถ ์ฝ๋ : ์ฌ๋/์ดํ๋ฆฌ์ผ์ด์ ์ ๋ก์ง์ ์ํ ๊ฒ์ด๋ค. ํ๋ก ํธ์๋ ๊ฐ๋ฐ์, ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ง๊ฒ ๋ ๋ฉ์์ง์์ ์ฌ์ฉ๋๋ค.
- ํ๋ก์ ํธ ๋ ๋ฒจ์ ์์ธ์ ๋๋ฉ์ธ ๋ ๋ฒจ์ ์์ธ๋ฅผ ๋ณ๊ฐ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
- ์๋ต์ด ํต์ผ๋ ์์ธ๋ฅผ ๋ง๋ค๊ธฐ ์ํด์๋ ๋ชจ๋ ๋๋ฉ์ธ ์์ธ๋ฅผ ๋ชจ์์ค ๊ฐ์ฒด๊ฐ ํ์ํ๋ค.
- ๊ทธ๊ฒ์ ์๋ฌ ํจ๋ค๋ฌ๋ผ ๋ถ๋ฅธ๋ค.
- api์ ์์ธ์ฒ๋ฆฌ์ ๊ด๋ จ๋ ์ ์ฒด์ ์ธ ํจํค์ง ๊ตฌ์กฐ๋ ์๋์ ๊ฐ๋ค.
๐com.example.umc9th
โโ ๐domain
โ โโ ๐review
โ โโ ๐controller
โ โโ ๐converter
โ โโ ๐dto
โ โโ ๐exception
โ โ โโ ReviewExceptio
โ โโ ๐service
โ โโ ๐command
โ โ โโ (I) ReviewCommandService
โ โ โโ ReviewCommandServiceImpl
โ โโ ๐query
โ โโ (I) ReviewQueryService
โ โโ ReviewQueryServiceImpl
โโ ๐global
โโ ๐apiPayload
โโ ๐code
โ โโ (I) BaseErrorCode
โ โโ (E) GeneralErrorCode
โโ ๐exception
โ โโ GeneralException
โโ ๐handler
โ โโ GeneralExceptionAdvice
โโ ApiResponse
RuntimeException์ ์์๋ฐ์ ์์ฑํ๋ค.
@Getter
@AllArgsConstructor
public class GeneralException extends RuntimeException {
private final BaseErrorCode code;
}ํ๋ก์ ํธ ๋ ๋ฒจ์ ์์ธ๋ฅผ ์์๋ฐ์ ์์ฑํ๋ค.
public class TestException extends GeneralException {
public TestException(BaseErrorCode code) {
super(code);
}
}- ์๋ง์ ์์น์์ ํฐ์ง ์์ธ๋ค์ ์๋ต์ ํต์ผํ ํต์ผ ๊ฐ์ฒด๊ฐ ํ์ํ๋ค.
- ์ด๋ฅผ ์์ธ ํธ๋ค๋ฌ๋ผ ๋ถ๋ฅธ๋ค.
- ์๋ฌ ํธ๋ค๋ฌ ์ฝ๋์ ์์ฑ์ ์๋์ ๊ฐ๋ค.
@RestControllerAdvice
public class GeneralExceptionAdvice {
// ์ ํ๋ฆฌ์ผ์ด์
์์ ๋ฐ์ํ๋ ์ปค์คํ
์์ธ๋ฅผ ์ฒ๋ฆฌ
@ExceptionHandler(GeneralException.class)
public ResponseEntity<ApiResponse<Void>> handleException(
GeneralException ex
) {
return ResponseEntity.status(ex.getCode().getStatus())
.body(ApiResponse.onFailure(
ex.getCode(),
null
)
);
}
// ๊ทธ ์ธ์ ์ ์๋์ง ์์ ๋ชจ๋ ์์ธ ์ฒ๋ฆฌ
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<String>> handleException(
Exception ex
) {
BaseErrorCode code = GeneralErrorCode.INTERNAL_SERVER_ERROR;
return ResponseEntity.status(code.getStatus())
.body(ApiResponse.onFailure(
code,
ex.getMessage()
)
);
}
}- ๊ธฐ๋ณธ์ ์ธ ์๋ฆฌ๋ ์ ์ํ ์์ธ๋ฅผ ๊ฐ์งํ๊ณ ๋ฏธ๋ฆฌ ์ ์ํด๋ ์๋ฌ ํธ๋ค๋ฌ ๋ก์ง์ ์คํํ๋ ๊ฒ์ด๋ค.
- ๋ฐ๋ผ์ ๋๋ฉ์ธ ์์ธ๋ ํ๋ก์ ํธ ์์ธ๋ฅผ ์์ํ๋ ๊ตฌ์กฐ์ด๊ณ , ์ด๋ฌํ ์์ธ๋ฅผ ๊ฐ์งํ๋ ๊ฒ์ ์๋ฌ ํธ๋ค๋ฌ์ ์ญํ ์ด๋ค.
- ์๋ฌ ์ฝ๋์ ๊ท์น์ ์ ํด๋๋ฉด ํ๋ก ํธ์๋ ๊ฐ๋ฐ์์ ์ํต์ด ํธํด์ง๋ค.
_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON000", "์๋ฒ ์๋ฌ, ๊ด๋ฆฌ์์๊ฒ ๋ฌธ์ ๋ฐ๋๋๋ค."),- ์์ ์๋ฌ ์ฝ๋์์ HttpStatus.INTERNAL_SERVER_ERROR๋ง ๋ณด๊ณ ๋ ์ค๋ฅ์ ํฐ ํ์ ์ ์ ์๋ค.
- ๋ ๋ฒ์งธ ์ธ์์ธ code๋ฅผ ๋ณด๊ณ ๋ ์์ธํ ์ค๋ฅ๋ฅผ ์ ์ ์๋ค.
- COMMON000 : common ์๋ฌ
- MEMBER4001 : ๋ฉค๋ฒ ๊ด๋ จ ์๋ฌ + 400๋ฒ๋(ํด๋ผ์ด์ธํธ ์ค๋ฅ) + ๊ทธ ์ค์์๋ 1๋ฒ ์๋ฌ
- ํด๋น ๊ท์น์ ์ ์ฉ์ ์๋์ ๊ฐ์ Enum์ ๊ตฌ์ฑํ ์ ์๋ค.
// Member Error
MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "์ฌ์ฉ์๊ฐ ์์ต๋๋ค."),
NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "๋๋ค์์ ํ์ ์
๋๋ค."),
// Article Error
ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "๊ฒ์๊ธ์ด ์์ต๋๋ค.");- ์ปจํธ๋กค๋ฌ ๋ด์ ์ ์ญ ์์ธ ์ฒ๋ฆฌ๊ธฐ(@RestControllerAdvice)๋ฅผ ํ
์คํธํ๊ธฐ ์ํ ๊ฒฝ๋ก๋ฅผ ํ๋ ์์ฑํ๋ค.
- GET /temp/exception
- ์ด๋ ๋ด๊ฐ ์์ฑํ ์ ์ญ ์์ธ ์ฒ๋ฆฌ๊ธฐ๊ฐ ์ ๋๋ก ์๋ํ๋์ง๋ฅผ ํ์ธํด๋ณด๊ธฐ ์ํ ํ ์คํธ ๊ฒฝ๋ก์ด๋ค.
- ์ฆ, ์์ธ์ฒ๋ฆฌ๊ฐ ์ ๋๋ก ๋๋์ง๋ฅผ ํ์ธํ๊ณ ์ถ๋ค๋ฉด GET /temp/exception๋ก ์์ฒญ์ ๋ณด๋ด๋ณด๋ฉด ๋๋ค.
- ์ด๋ ์ฟผ๋ฆฌ์คํธ๋ง flag๋ฅผ ์ฌ์ฉํด ์ค๋ก์ง flag=1์ผ ๋๋ง ์์ธ๊ฐ ํฐ์ง๋๋ก ํ๋ค.
- ์ด๋ ํด๋น ์์ธ ์ฒ๋ฆฌ ๊ฒฝ๋ก๊ฐ ์๋๋ ์ ์์ ์ธ ๊ฒฝ๋ก์ด๋ฉฐ ์์ธ๋ฅผ ํฐํธ๋ฆฌ๋ฉด ํด๋น ์์ธ๊ฐ ์ ๋๋ก ์คํจ api์ ๋ด๊ฒจ ์ ๋ฌ๋๋์ง๋ฅผ ํ์ธํ๊ธฐ ์ํจ์ด๋ค.
@RequestParam๋ ์ฟผ๋ฆฌ ์คํธ๋ง์ ๋ฐ์์ค๊ธฐ ์ํ ์ด๋
ธํ
์ด์
์ด๋ค.
@RestController
@RequestMapping("/temp")
@RequiredArgsConstructor
public class TempRestController {
...
// ์ ์ญ ์์ธ ์ฒ๋ฆฌ๊ธฐ์ ํ
์คํธ ๊ฒฝ๋ก
@GetMapping("/exception")
public ApiResponse<TestResDTO.Exception> exception(@RequestParam Long flag) {
return null;
}
}Service๋ฅผ ์์ฑ ํ ๋๋ ์๋์ ๊ฐ์ ๊ท์น์ ๋ฐ๋ฅธ๋ค.
- ์กฐํ์ ๋ํ ์์ฒญ์ query ํด๋๋ก, ์ด์ธ์ ์์ฒญ์ command ํด๋๋ก ๊ตฌ๋ถํ๋ค.
- ์๋น์ค๋ฅผ ๋ง๋ค ๊ฒฝ์ฐ ์ธํฐํ์ด์ค๋ฅผ ๋จผ์ ๋๊ณ ์ด๋ฅผ ๊ตฌ์ฒดํ ํ๋ค.
- ex) TempQueryService ์ธํฐํ์ด์ค๋ฅผ ๋จผ์ ๋ง๋ค๊ณ ์ด์ ๋ํ Impl ๊ตฌ์ฒดํ ํด๋์ค๋ฅผ ๋ง๋ ๋ค.
- ์ปจํธ๋กค๋ฌ๋ ์ธํฐํ์ด์ค๋ฅผ ์์กดํ๋ฉฐ ์ค์ ์ธํฐํ์ด์ค์ ๋ํ ๊ตฌ์ฒดํ ํด๋์ค๋ ์คํ๋ง๋ถํธ์ ์์กด์ฑ ์ฃผ์ ์ ์ด์ฉํ๋ค!
์์ ๊ฒฝ์ฐ ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๋ ์ผ๋์ผ๋ก ๋งคํ๋๋ค. ๊ทธ๋ผ ์ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํด์ผ ํ ๊น?
- ์์กด์ฑ ์ญ์ ์์น
- ์ปจํธ๋กค๋ฌ๋ ๊ตฌ์ฒด์ ์ธ ๊ตฌํ ํด๋์ค๊ฐ ์ด๋ค ์์ผ๋ก ๋์ํ๋์ง ์ ํ์๊ฐ ์๋ค.
- ์ธํฐํ์ด์ค์ ์ ์๋ ์ธ์๊ฐ๊ณผ ๋ฐํ ํ์ ์ ๋ํ ์ ๋ณด๋ง์ ์๊ณ ์๋ค.
- ํ ์คํธ ์ฉ์ด์ฑ
- ๋ง์ฝ ์ปจํธ๋กค๋ฌ๊ฐ ๊ตฌ์ฒด์ ์ธ ๊ตฌํ์ฒด์ ์ง์ ์์กดํ๋ค๋ฉด ์ปจํธ๋กค๋ฌ๋ฅผ ํ ์คํธํ ๋ Service๊ฐ ์์กดํ๋ ๋ ํ์งํ ๋ฆฌ๋ DB๊ฐ ๋ชจ๋ ํ์ํ๊ฒ ๋๋ค.
- ๊ทธ๋ฌ๋ ์ปจํธ๋กค๋ฌ๊ฐ ์ธํฐํ์ด์ค์ ์์กดํ๊ฒ ๋๋ฉด ํ ์คํธ ์ ๊ฐ์ง ๊ตฌํ์ฒด๋ฅผ ์ฝ๊ฒ ์ฃผ์ ํ ์ ์๋ค.
- ๋ง์ฝ PaymentService๋ผ๋ ๊ฒฐ์ ์๋น์ค ์ธํฐํ์ด์ค๊ฐ ์๋ ๊ฒฝ์ฐ ๊ฒฐ์ฌ ๋ฐฉ์์ ๋ฐ๋ผ ๊ตฌํ์ฒด๊ฐ ๋๋ ์ ์๋ค.
- KakaoPaymentService
- TossPaymentService
- NaverPaymentService
- ์ด๋ ์ปจํธ๋กค๋ฌ์์ ์ฌ์ฉ์์ ๊ฒฐ์ฌ ์๋จ์ ๋ํ ์ ๋ณด๊ฐ ๋ค์ด์ค๋ฉด ์ ์ ํ ๊ตฌํ์ฒด๋ฅผ ๋์ ์ผ๋ก ์ ํํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
public interface TestQueryService {
void checkFlag(Long flag);
}checkFlag ๋ฉ์๋๋ flag๊ฐ 1์ธ ๊ฒฝ์ฐ์๋ง ์์ธ๋ฅผ ํฐํธ๋ฆฐ๋ค.
@Service
@RequiredArgsConstructor
public class TestQueryServiceImpl implements TestQueryService {
@Override
public void checkFlag(Long flag){
if (flag == 1){
throw new TestException(TestErrorCode.TEST_EXCEPTION);
}
}
}@GetMapping("/exception")
public ApiResponse<TestResDTO.Exception> exception(
@RequestParam Long flag
) {
testQueryService.checkFlag(flag); // flag๊ฐ 1์ด๋ผ๋ฉด ์ด ๋ถ๋ถ์์ ์์ธ๊ฐ ๋ฐ์ํ๋ค!!
GeneralSuccessCode code = GeneralSuccessCode.OK;
return ApiResponse.onSuccess(code, TestConverter.toExceptionDTO("This is Test!"));
}- Spring์์ ์ ์ญ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ์ด๋ ธํ ์ด์ ์ผ๋ก ๋ชจ๋ ์ปจํธ๋กค๋ฌ์ ์์ธ๋ฅผ ํ ๊ณณ์์ ์ฒ๋ฆฌํ ์ ์๋ค.
- @ExceptionHandler์ ํจ๊ป ์ฌ์ฉ๋์ด ๊ณตํต๋ ์๋ฌ ์๋ต ํ์์ ์ ๊ณตํ๋ค.
- @ControllerAdvice + @ResponseBody์ ์กฐํฉ๊ณผ ๋์ผํ ๊ธฐ๋ฅ์ ํ๋ค.
- ๋ฐ๋ณต๋๋ ์ฝ๋(์์ฑ์, getter/setter, toString ๋ฑ)๋ฅผ ์๋์ผ๋ก ์์ฑํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
- @Getter, @Setter, @Builder, @AllArgsConstructor ๋ฑ ์ด๋ ธํ ์ด์ ์ผ๋ก ์ฝ๋ ๊ฐ๋ ์ฑ์ ๋์ธ๋ค.
- ์ปดํ์ผ ์์ ์ ์ค์ ์ฝ๋๊ฐ ์ถ๊ฐ๋๋ฏ๋ก ์คํ ์๋์๋ ์ํฅ์ ์ฃผ์ง ์๋๋ค.
- public static class๋ Lombok๊ณผ ํจ๊ป ์ฐ์ฌ ์ ์ฐํ๊ณ ๊ณ์ธต์ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค๊ธฐ ์ข๋ค.
- record๋ ์๋ฐ 16+์์ ๋์ ๋ ๋ถ๋ณ(immutable) ๋ฐ์ดํฐ ์ ๋ฌ์ฉ ํด๋์ค์ด๋ฉฐ ์ฝ๋๊ฐ ๋งค์ฐ ๊ฐ๊ฒฐํ๋ค.
- ๊ทธ๋ฌ๋ record๋ ํ๋ ์์ ์ด๋ ์์์ด ๋ถ๊ฐ๋ฅํ๋ฏ๋ก ๋จ์ DTO์๋ง ์ ํฉํ๋ค.