API 개발 기본

김슬기·2022년 11월 4일
0

회원 등록 API

  • 기존 패키지들과 API패키지를 따로 관리하는것이 좋음
    • 기존패키지들은 HTML로 결과값이 관리되는 반면에 API는 json형태로 결과값을 관리하기 때문
  • @RestController
    • @Controller와 @Responsebody두가지 애노테이션 역할을 해줌
      • @Responsebody는 데이터를 json, xml으로 바꿔서 보내주는 역할
@RestController
@RequiredArgsConstructor
public class MemberApiController {
    private final MemberService memberService; // 멤버서비스 = 멤버 도메인의 엔티티를 다루는 장고의 view와 비슷

@PostMapping("/api/v1/members") // api호출 주소
    public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member)
    { // 이렇게하면 json.body내용을 member에 쫙 매핑해준다.
        Long id = memberService.join(member);
        return new CreateMemberResponse(id);
    }

    @Data
    static class CreateMemberResponse {
        private Long id;
        public CreateMemberResponse(Long id) {
            this.id = id;
        }
    }
}
  • 등록V1:요청 값으로Member엔티티를 직접 받는다.
  • 문제점
  • 엔티티에 API검증을 위한 로직이 들어간다. (@NotEmpty등등)
    • 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
    • 실무에서는 회원 엔티티를 위한API가 다양하게 만들어지는데,한 엔티티에 각각의API를
      위한 모든 요청 요구사항을 담기는 어렵다.
    • 엔티티가 변경되면API스펙이 변한다.
      • 엔티티의 속성하나를 변경할 경우 이런 방식의 API의 호출은 깨져버리게된다.
  • 그래서 문제점을 해결하기위해 API요청 스펙에 맞추어 별도의DTO를 파라미터로 받는다.
    • 외부에서 받아온 json형식을 그대로 사용하는 방식을 버리고 DTO방식 채택해야함

      @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;
      }
  • 이렇게 할 경우 엔티티에 데이터를 넣어서 API를 호출하는게 아니라 자체적인 클래스안에 json 데이터를 넣어서 그 클래스 인스턴스로 api호출이라 엔티티의 내용이 바뀌어도 상관없다
  • 추가로 유지보수에 장점이있음
    • 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;
}
  • @PathVariable ⇒ 이친구는 이제 위의 url에 매개변수에 인자 집어넣은것을 api클래스 안에서 사용할 수 있다~
  • @AllArgsConstructor는 모든 멤버를 인자로 받아 객체생성하는 생성자를 만들어줌
  • 이것으로 인해 모든 인자를 넘겨줘야함
@Transactional
public void update(Long id, String name) {
    Member member = memberRepository.findOne(id);
    member.setName(name);
}// 멤버서비스에 업데이트 만들어주기
  • 하지만 근데 여기선 PUT을쓰긴했는데 부분수정이니깐 PATCH로하는게….?

회원 조회 API

@GetMapping("/api/v1/members")
public List<Member> membersV1() {
    return memberService.findMembers();
}
  • 이렇게하면 만들기는 쉽다
  • 하지만 보안상 문제가 발생
    • 프론트에서 원하는 데이터만 넘기는것이 아닌 엔티티를 전부 넘기기때문에 엔티티가 노출이 되어버린다~~~
    • @JsonIgnore을 엔티티의 멤버에 작성하면 그 멤버는 날라가지않음
    • 하지만 이건 이정보가 필요할 경우 사용불가이므로 안된다..
  • 그래서~
@GetMapping("/api/v2/members")
public Result membersV2() {
    List<Member> findMembers = memberService.findMembers(); //엔티티 -> DTO 변환
    List<MemberDto> collect = findMembers.stream()
            .map(m -> new MemberDto(m.getName()))
            .collect(Collectors.toList());// 멤버 리스트를 멤버DTO리스트로 바꿔주는것
    return new Result(collect);
}
@Data
@AllArgsConstructor
static class Result<T> {
    private T data;
}
@Data
@AllArgsConstructor
static class MemberDto {
    private String name;
}
  • 여기서 return new Result(collect); Result로 감싸주는 이유는 다루기 쉽게하려고
    • 왜냐면 저기서 그냥 콜렉트만 반환하면 json배열로 반환되는데 이것에는 추가로 원하는 값을 넣기가 힘들다

    • 그래서 저렇게 Result로 감싼다음에 멤버로 private int count 이런식으로 추가해서 사용가능하지않을까?

      @GetMapping("/api/v2/members")
      public Result membersV2() {
          List<Member> findMembers = memberService.findMembers(); //엔티티 -> DTO 변환
          List<MemberDto> collect = findMembers.stream()
                  .map(m -> new MemberDto(m.getName()))
                  .collect(Collectors.toList()); // 멤버 리스트를 멤버DTO리스트로 바꿔주는것
          return new Result(collect.size(), collect);
      }
      @Data
      @AllArgsConstructor
      static class Result<T> {
          private int count;
          private T data;
      }
      @Data
      @AllArgsConstructor
      static class MemberDto {
          private String name;
      }


되네~~

profile
낭만그리고김슬기

0개의 댓글