Web/JPA

EP1. 회원관리 API 만들기

@RequestBody를 이용해 Json 요청을 DTO로 받을 수 있다.

그리고 @RestController를 클래스 레벨에 적어주면 반환할때 DTO를 반환하면 JSON으로 변환해준다.

 

자세한 내용은 MVC 정리에서 볼 수 있다.

 

2021.03.29 - [Web/MVC] - EP6. HTTP 요청 데이터 처리

 

EP6. Http 요청 데이터 처리하기

Http 헤더 꺼내기 docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-annarguments Web on Servlet Stack Spring Web MVC is the original web framework built on the Servlet API and..

ksabs.tistory.com

2021.03.30 - [Web/MVC] - EP7. HTTP 응답 데이터 처리

 

EP7. HTTP 응답 데이터 처리

HTTP 응답 데이터를 만드는 방법에는 세가지가 있다. 정적 리소스 뷰 템블릿 사용 HTTP 메세지 사용 정적리소스 웹 브라우저에 정적인 HTML, CSS, JS를 제공할 때는 정적 리소스를 사용한다. 스프링부

ksabs.tistory.com

 

 

회원 등록 API

    @PostMapping("/api/v2/members")
    public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request) {
        Member member = new Member();
        member.setName(request.getName());

        Long id = memberService.join(member);
        return new CreateMemberResponse(id);

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


    @Data
    static class CreateMemberResponse {
        private Long id;

        public CreateMemberResponse(Long id) {
            this.id = id;
        }
    }

 

요청 데이터를 받을 때나 응답 데이터를 보낼 때 엔티티를 직접 사용하지 않는다.

 

요청 데이터에서 객체를 직접 받을 때

다양한 요청데이터를 처리할때 사용하지 않는 필드들이 있을 수 있다.

그러나 엔티티 자체에서 여러 API의 요구사항을 충족할 수는 없다.

 

그래서 API 요청 스펙에 맞추어 별도의 DTO를 파라미터로 받아 사용하는 것이다.

회원등록 API에서는 CreateMemberRepquest를 사용하여 요청 데이터를 받았다.

 

 

 

응답 데이터에서 객체를 직접 반환할 때

엔티티의 필드의 이름이 바뀌었을때 기존의 API 스펙을 깰 수 있다.

예를들어, 기존에 JSON 으로 { "name" : "hello"} 이렇게 name을 응답하기로 API 스펙을 정했지만

엔티티의 필드의 이름이 name에서 username으로 바뀌게 되면 클라이언트는 약속된 JSON 데이터를 받지 못하게 된다.

 

 

 

 

 

회원 수정 API

    @PutMapping("/api/v2/members/{id}")
    public UpdateMemberResponse updateMemberV2(
            @PathVariable("id") Long id,
            @RequestBody @Valid UpdateMemberRequest request) {

        memberService.update(id, request.getName());
        Member findMember = memberService.findOne(id);
        return new UpdateMemberResponse(findMember.getId(), findMember.getName());
    }
    
    
    @Data
    static class UpdateMemberRequest {
        private String name;
    }

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

 

메소드는 PUT으로 한다.

멱등을 고려할 때 update는 POST보단 PUT을 사용하는 것이 좋다.

 

그런데 PUT은 전체 update를 할때 사용하는 것이고, 부분 update를 할 때는 PATCH나 POST를 사용하는 것이 REST 스타일에 맞다.

 

 

PathVariable로 회원의 아이디를 받아온다.

그리고 "변경감지"를 이용해서 데이터를 수정한다.

    @Transactional
    public void update(Long id, String name) {
        Member member = memberRepository.findOne(id);
        member.setName(name);

    }

@Transactional, setName

트랜잭션 안에서 setName으로 데이터를 수정하면 JPA의 변경감지 기능이 작용해 Update 쿼리가 나간다.

 

 

회원 등록때와 마찬가지로 요청과 응답데이터에 DTO를 사용한다.

update에서 member를 반환할 수 있지만 커맨드와 쿼리를 분리한다는 원칙에 의해서 반환하지 않거나 id만 반환하게 만들 수 있다.

 

UpdateMemberReponse DTO를 따로 만들어서 반환해준다.

 

 

 

 

 

 

회원 조회 API

    @GetMapping("/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);
    }

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

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

여기서도 엔티티를 직접 반환하지 않고 DTO로 감쌌다. (MemberDto)

 

그리고 또한 Result로 한번 더 감쌌다.

 

Result로 한번 더 감싼 이유

 

Result로 감싸지 않았을 때는 member들의 리스트가 Json 형태로 반환된다.

[
    {
        "id": 1,
        "name": "new_hello",
        "address": null,
        "orders": []
    },
    {
        "id": 2,
        "name": "member1",
        "address": {
            "city": "서울",
            "street": "asdw",
            "zipcode": "123"
        },
        "orders": []
    },
    {
        "id": 3,
        "name": "member2",
        "address": {
            "city": "인천",
            "street": "w4wf",
            "zipcode": "12345"
        },
        "orders": []
    }
]

여기서 만약 추가 요구사항으로 "Json 데이터에 엔티티의 개수를 포함시켜주세요" 가 추가되면 어떻게 해야할까?

 

여기서 count를 추가하면 JSON 스펙에 맞지 않게된다. (추가할 수 없다.)

 

 

 

만약 여기서 Json을 한번 더 감싸준다면?

{
    "count": 3
    "data": [
        {
            "name": "new_hello"
        },
        {
            "name": "member1"
        },
        {
            "name": "member2"
        }
    ]
}

이렇게 count 와 data로 구분되어 JSON 스펙에 맞게 요구사항을 추가할 수 있게 된다.