이번 강의는 지금까지 배운 내용들이 한 번에 정리되는 느낌의 강의였다.
핵심 키워드는 딱 세 가지다.
27강에서 우리는 이런 코드를 사용했다.
@PostMapping
@Transactional
public ResponseEntity<RsData<PostWriteResBody>> write(@Valid @RequestBody PostWriteReqBody form) {
Post post = postService.write(form.title, form.content);
return new ResponseEntity<>(
new RsData<>(
"201-1",
"%d번 글이 작성되었습니다.".formatted(post.getId()),
new PostWriteResBody(
postService.count(),
new PostDto(post)
)
),
HttpStatus.CREATED
);
}
이 방식의 문제점은 명확했다.
👉 “HTTP 응답코드 하나 바꾸려고 이 정도 코드가 필요한가?”
그래서 나온 결론이 바로 AOP 다.
201-1)를 기준으로 상태 코드 자동 설정@PostMapping
@Transactional
public RsData<PostWriteResBody> write(@Valid @RequestBody PostWriteReqBody form) {
Post post = postService.write(form.title, form.content);
return new RsData<>(
"201-1",
"%d번 글이 작성되었습니다.".formatted(post.getId()),
new PostWriteResBody(
postService.count(),
new PostDto(post)
)
);
}
이제 컨트롤러는 RsData만 신경 쓴다.
public record RsData<T>(String resultCode, int statusCode, String msg, T data) {
public RsData(String resultCode, String msg) {
this(resultCode, msg, null);
}
public RsData(String resultCode, String msg, T data) {
this(
resultCode,
Integer.parseInt(resultCode.split("-", 2)[0]),
msg,
data
);
}
}
this(resultCode, msg, null);
즉 우리가 직접 정의하지 않아도
RsData(String resultCode, int statusCode, String msg, T data)
이 생성자는 항상 존재하고,
우리는 그걸 호출하고 있는 것이다.
Integer.parseInt(resultCode.split("-", 2)[0])
"201-1" → 201"404-1" → 404resultCode 규칙만 지키면
HTTP 상태 코드도 자동으로 결정된다.
@Aspect
@Component
public class ResponseAspect {
private final HttpServletResponse response;
public ResponseAspect(HttpServletResponse response) {
this.response = response;
}
“이 클래스는 AOP용 클래스입니다” 라고 스프링에게 알려주는 표시
즉,
HTTP 응답코드(200, 201, 404 등)는
컨트롤러 리턴값이 아니라 응답 객체(HttpServletResponse) 에 설정된다.
그래서 상태 코드를 바꾸려면
response.setStatus(201);
를 직접 호출해야 하고,
이를 위해 HttpServletResponse를 주입받는다.
@Around("""
(
within(@RestController *) &&
(
@annotation(GetMapping) ||
@annotation(PostMapping) ||
@annotation(PutMapping) ||
@annotation(DeleteMapping) ||
@annotation(RequestMapping)
)
)
""")
이 조건의 의미는 딱 하나다.
👉 “모든 RestController의 요청 메서드를 가로채라”
즉,
이 붙은 메서드는
실행 전·후로 이 AOP가 자동 실행된다.
Object proceed = joinPoint.proceed();
이 줄이 의미하는 바는 아주 중요하다.
즉,
@PostMapping
public RsData write(...) { ... }
이 메서드가
이 시점에서 실행되고 그 결과가 proceed에 담긴다.
if (proceed instanceof RsData<?> rsData) {
response.setStatus(rsData.statusCode());
}
JSON 본문은 건드리지 않는다.
return proceed;
📸 여기에 Postman 201 Created 결과 이미지 추가
1️⃣ 컨트롤러 메서드 실행
2️⃣ RsData 반환
3️⃣ ResponseAspect가 실행됨
4️⃣ resultCode → statusCode 계산
5️⃣ HttpServletResponse에 상태 코드 설정
6️⃣ JSON 응답 전송
@Transactional → AOP
@Validated → AOP
@ResponseAspect → AOP
즉, 스프링은 이미 AOP 기반 프레임워크다.
public RsData<PostWriteResBody> write(@Valid @RequestBody PostWriteReqBody form)
REST API에서는
👉 REST API + JSON = @RequestBody 필수
이번 강의는
단순히 AOP 문법을 알려준 게 아니라,
“왜 이런 구조를 쓰는지”
“언제 단순하게 가도 되는지”
를 같이 보여준 강의였다.
지금까지 배운 RsData, HTTP 상태 코드, AOP가
하나의 구조로 딱 맞아떨어진 느낌이다.