SpringBoot로 API 개발 시 최적화에 대해서 - 1

2024. 9. 6. 00:32·스프링

등록, 생성, 추가 관련 API 

보통 POST Method 를 이용하여 요청을 처리한다.

@PostMapping("/api/v1/members")
public CreateMemberResponse saveMember(@RequestBody @Valid Member member){
	...
}

문제:  엔티티를 RequestBody 에 직접 매핑

이유:

  • 프레젠테이션을 위한 로직에 엔티티가 들어간 부분은 유지 보수에 좋지 않다.
  • RequestBody 로 매핑되는 파라미터는 검증을 할 수 있다. 근데  엔티티를 RequestBody로 직접 매핑시켜버리면 엔티티에 검증을 위한 로직이 추가된다. 예를 들어 @NotNull 이 엔티티 필드에 추가된다는 것이다. 
  • API 는 수십~수백개가 존재할 텐데 그 때마다 API 의 요구사항을 엔티티에 반영하는 것은 무리이다.
  • 엔티티가 변경되면 API 스펙이 변경될 수 있는 문제가 존재한다.

 

@PostMapping("/api/v2/members")
public CreateMemberResponse saveMember(@RequestBody @Valid CreateMemberRequest reqeust){
	...
}

해결: 엔티티 대신 DTO 클래스를 RequestBody에 매핑

 

수정 관련 API

@PutMapping("/api/v2/members/{id}")
public UpdateMemberResposne updateMemberV2(@PathVariable("id") Long id, @RequestBody @Valid UpdateMemberRequest request){
	memberService.update(id, request.getName());
    Member findMember = membeService.findOne(i);
    return new UpdateMemberResponse(...);
}

수정 관련 API 에 대해서 어떤 Http Method 를 사용하는 지가 문제일 수 있다.

1. PUT vs POST : 멱득성

  • POST 는 같은 요청을 여러 번 보냈을 때 다른 결과를 반환한다.
  • PUT 은 같은 요청에 대해서 항상 같은 결과를 반환한다.

2. PUT vs PATCH : 전체와 부분

  • PUT 은 전체를 업데이트할 때 사용한다.
  • PATCH 는 부분을 업데이트할 때 사용한다.
  • 하지만 이 둘을 명확하게 구분한 것은 어려운 일이다. 예시를 들어 보자. User 엔티티에 id 와 name 만 있었다고 가정을 해보자. 이름을 변경하기 위해서는 API는 PUT 으로 만들어야 한다. 근데 시간이 흘러 age 라는 필드가 user 에 추가되었다. 원래 name 만 변경하는 API 는 PATCH 로 변경되어야 한다. 이렇게 되면 API 의 method 를 변경해야 하는데 이런 작업이 쉬운 것은 아니다.

서비스 계층의 수정 메소드에서 반환 값 설정없이 void 로 하는 이유

  • 목적에 맞지 않다. 수정하는 서비스에 대해서는 해당 객체를 반환하는 것은 조회도 포함하게 되는데 수정과 조회를 함게 하는 형태가 된다. 이런 형태는 유지 보수를 어렵게 한다.
memberService.update(id, request.getName());
Member findMember = memberService.findOne(id);
  • 수정 관련 API 에서 응답 값을 보내줘야 하므로 서비스 계층의 조회 메소드를 호출하여 값을 반환해 준다.

조회 관련 API

Get Method를 사용하여 요청을 매핑한다.

@GetMappiing("/api/v1/members")
public List<Member> membersV1(){
	return MemberService.findMembers();
}

문제: 응답 값으로 엔티티를 직접 외부에 노출한다.

이유:

  • RequsetBody 로 엔티티를 사용할 때와 같은 문제가 발생한다.
  • 엔티티의 모든 값이 노출된다. -> 필요한 값만 노출되어야 한다.
  • 추가로 컬렉션을 직접 반환할 시 향후 API 스펙을 변경하기가 어렵다.
@GetMappiing("/api/v2/members")
public Result membersV2(){
	List<Member> findMembers = MemberService.findMembers();
	List<MemberDto> collect = findMembers.stream()
			.map(m -> new MemberDto(m.getName()))
			.collect(Collectors.toList());
			
	return new Result(collect);
}

해결: 별도의 응답 DTO 클래스를 생성하여 Result 객체에 담아서 반환한다.

  • 엔티티 변경 시 API 스펙 변경이 없다.
  • 엔티티가 노출되지 않는다.
  • Result 클래스를 감싸서 향 후 필요한 필드를 추가할 수 있다.

참고자료

인프런 이영한 - 스프링 부트와 JPA 활용 2

'스프링' 카테고리의 다른 글

[Test] 테스트 컨테이너 설정  (0) 2025.04.02
낙관적 락(Optimistic Lock), 비관적 락(Pessimistic Lock)  (1) 2025.03.19
'스프링' 카테고리의 다른 글
  • [Test] 테스트 컨테이너 설정
  • 낙관적 락(Optimistic Lock), 비관적 락(Pessimistic Lock)
khw7385
khw7385
khw7385 님의 블로그 입니다.
  • khw7385
    khw7385 님의 블로그
    khw7385
  • 전체
    오늘
    어제
    • 분류 전체보기 (43)
      • 코딩테스트 (7)
      • 자바 (3)
      • 스프링 (3)
      • cs (7)
        • 자료구조 (3)
        • 알고리즘 (1)
        • 객체지향 (3)
      • 개발일지 (6)
        • 트러블슈팅 (1)
      • 데이터베이스 (3)
        • Redis (2)
        • MySQL (1)
      • 기타 (2)
      • devops (6)
      • LG CNS AM INSPIRE (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
khw7385
SpringBoot로 API 개발 시 최적화에 대해서 - 1
상단으로

티스토리툴바