JPA 활용편 2 - API 개발 기본

VANS·2022년 5월 16일
1

강의

목록 보기
1/1

본 글은 강의를 들으며 메모해놓은것을 정리하기 위한 글입니다.
잘못된 정보가 있다면 댓글로 편하게 정정 부탁드리겠습니다. 🙏

  강좌에서는 API 패키지를 만들때 주로 템플릿 엔진으로 랜더링을 하는 컨트롤러API 스타일의 컨트롤러를 아예 분리하기를 권장한다. 권장하는 이유는 예외처리를 하게 될때 보통 패키지 단위로 하게되는데, View단과 API단의 공통요소가 다르기 때문이다.

회원등록 API

  @Controller@Responsebody를 보통 컨트롤러에 적용하지만 이 둘을 합친 @RestController를 적용해도 무방하다.
(@RestController : RestAPI 스타일로 만들기 위해 활용할 수 있는 Spring Annotation이다.)


@PostMapping("url")
public CreateMemberResponse saveMember(@RequestBody @Valid Member member) { //Entity를 파라미터로 받고 있다.
	//회원등록 로직
}

@Data
static class CreateMemberResponse {
	private Long id;
    
    public CreateMemberResponse(Long id) {
    	this.id = id;
    }
}

  위와 같은 로직이여도 회원가입 기능은 문제없이 작동한다. 하지만 큰 문제점이 하나 있는데, 바로 Entity와 API가 1:1 맵핑되어 있다는 것이다. 이는 만약 Entity의 상태값의 변수명등을 변경하게 된다면 API spec도 함께 변경이 되어비리기 때문에 Side Effect와 같은 큰 장애를 발생시킬 가능성이 있다.

Side Effect의 가능성을 없애버리려면 어떻게 바꿔야 할까?


@PostMapping("url")
public CreateMemberResponse saveMember(@RequestBody @Valid CreateMemberRequest request) {
	//회원등록 로직
}

@Data
static class CreateMemberRequest {
	@NotEmpty
	private String name;
}

  기존에 파라미터로 Entity를 받던 형식을 Request DTO로 받는 형식으로 변경하여 Entity와 API spec과의 관계를 끊어냈다.
(Entity - API 👉 Entity - DTO - API)
만약 이런 상황에서 요청받는 데이터중 namenull값이 아니여야 한다면, Entity가 아닌 Request DTO에서 해당 항목값에 NotEmpty를 추가해주면 된다.
(@NotEmpty : Schema Table에서 NOT NULL 처리하는것과 유사한 기능의 Annotation)


회원수정 API

  회원정보를 수정하는 API 기능을 만들때 HTTP PUT요청에 적합한 @PutMapping으로 작성한다. 그리고 PUT으로 호출하게 되면 데이터를 수정하여 결과를 대체하므로 계속 수정 요청을 하면 최종 결과는 계속 대체되므로 동일하다. 이를 멱등하다 라고 표현하기도 하고, 비슷한 맥락으로 GETDELETE 또한 멱등하다라고 표현할 수 있다.


@PutMapping("url/{id}")
public UpdateMemberResponse updateMember(@PathVariable("id) Long id, @RequestBody @Valid UpdateMemberRequest request) { 
	//회원수정 로직
}

@Data
static class UpdateMemberRequest {
	private String name;
}

@Data
@AllArgsConstructor
static class UpdateMemberResponse {
	private Long id;
    private String name;
}

  회원등록때 썼던 DTO는 등록API와 수정API의 spec이 다르므로 동일하게 활용하면 안된다. 그러므로, 회원수정에 맞는 DTO(UpdateMemberRequest, UpdateMemberResponse)를 만들어야 한다.

아울러, 수정API의 service단에서는 가급적이면 변경감지를 쓰는것을 권장한다. 아래와 같이 말이다.

public class MemberService {

	@Transactional
    public void update(Long id, String name) {
   		Member member = memberRepository.findOne(id); //id를 DB에서 찾아오면서 1차 캐시에 member가 올라간다.
        member.setName(name); 				//Entity와 스냅샷의 변경점이 생긴것을 감지한다.
    }

}

  위 코드에서 사실 Member로 반환해도 로직에는 이상없지만 커맨드쿼리를 철저하게 분리하는것이 유지보수성 증대면에서 탁월하다.
(이부분은 아직 100% 이해가는 부분이 아니라, 참고만 부탁드립니다.)


회원조회 API

  회원정보 조회할때 단순하게 아래 코드처럼 구현할 수도 있다.


@GetMapping("url")
public List<Member> members() {
	return memberService.findMembers();
}

  하지만 이렇게 구현을 해놓고 요청을 하면, Member Entity가 반환되면서 개인정보와 같이 보안이 중요한 정보들도 같이 반환하게 되버린다. 물론 @Jsonignore를 Entity에서 숨기고자 하는 상태값에 적용하여 조회데이터를 수정하여 반환할수도 있지만, 이렇게 되면 Entity에 화면기능이 추가된것이므로 순수하지가 않게 된다.

필요한 내용만 반환해서 조회하게 하려면 어떻게 바꿔야할까?


@GetMapping("url")
public Result members() {
	//회원조회 로직
    return new Result(collet);
}

@Data
@AllArgsConstructor
static class Result<T> {
	private T data;
}

@Data
@AllArgsConstructor
static class MemberDto {
	private String name;
}

  바로 Member List를 반환하지 않고, MemberDto로 변환하여 조회에 필요한 data만 담아서 훨씬 안전하게 반환할 수 있다.(유지보수 측면에서도 훨씬 낫다.)

profile
코딩도 점진적 과부화

0개의 댓글