Spring/[인프런 김영한 실전 스프링 부트와 JPA 활용 2

[인프런 김영한 실전 스프링 부트와 JPA 활용 2 - API 개발과 성능 최적화] API 개발 기본

h2boom 2024. 9. 4. 16:16

API 개발

  • API tool - postman

 

  • API 개발 시 템플릿 엔진(화면) 사용하는 Controller와 API를 사용하는 Controller를 패키지를 통해 분리하는 것이 좋다.
    • 공통 처리를 할 때 패키지 단위로 많이 하는데 화면과 API는 공통 처리해야 할 부분이 많이 다르기 때문

 

  • @ResponseBody : 자바 객체를 json 기반의 HTTP Body로 변환한다.
    • 주로 데이터 자체를 바로 Json을 XML로 보낼 때 사용한다.
  • @RequestBody : json 기반의 HTTP Body를 자바 객체로 변환한다.
  • @RestController : @Controller + @ResponseBody 어노테이션을 합친 어노테이션이다.
  • @Data : @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor를 합쳐놓은 어노테이션이다.
    • POJO (Plain Old Java Objects)와 bean과 관련된 모든 보일러플레이트(boilerplate = 재사용 가능한 코드)를 생성한다.

 

// 1번 예시
@PostMapping("/api/v1/members")
	public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
	Long id = memberService.join(member);
	
	return new CreateMemberResponse(id);
}

// 2번 예시
@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);
}
  • 1번 예제의 잘못된 점은 Member 엔티티를 API에서 사용하고 있다.
    • 엔티티는 여기저기서 많이 사용되기에 바뀔 확률이 높다.
    • 엔티티를 외부에서 Json을 바인딩 받는 용도로 사용해서는 안된다.
    • 엔티티가 변경되면 API 스펙도 변경되기에 올바른 설계 방식이 아니다.
      => 2번 예제와 같이 엔티티와 API 스펙을 명확히 분리하기 위한 별도의 DTO를 만들어서 사용해야 한다.
    • 엔티티를 매개변수로 받으면 엔티티의 어떤 필드가 값으로 넘어오는지 확인하지 않는 이상 알 수 없다.
      • DTO를 사용하면 어떤 값들이 매개변수로 넘어오는지 알기 쉽다.
    • Validation 같은 경우에도 각 API 스펙별로 요구하는 검증 조건이 다를 수 있기에 각 API에 맞는 DTO를 생성하고 Validation 조건을 지정해야 한다.

 

  • API 설계 시 주의 사항
    • API 설계 시 엔티티를 매개변수로 받지 말 것!!!
    • 엔티티를 외부에 노출시키지 말 것!!!!

 

  • 일반적으로 JSON의 키 값은 자바 객체의 필드명과 자동으로 매핑된다.

 

  • @JsonIgnore : 직렬화, 역직렬화에 사용되는 논리적 속성 값을 무시할 때 사용한다.
    • @JsonIgnore가 사용된 필드는 JSON 데이터에 포함되지 않는다.

 

@GetMapping("/api/v2/members")
public Result memberV2() {
	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;
}
  • Result라는 Object 타입으로 한 번 감싸서 반환하는 이유
    • 컬렉션을 바로 반환하면 JSON 배열 타입이 되기 때문에 유연성이 떨어진다.

 

//컬렉션(List)으로 반환한 JSON 결과
[
    {
        "name": "hello"
    },
    {
        "name": "member1"
    },
    {
        "name": "member2"
    }
]

//컬렉션을 Object로 감싼 후 반환한 JSON 결과
{
    "data": [
        {
            "name": "hello"
        },
        {
            "name": "member1"
        },
        {
            "name": "member2"
        }
    ]
}
  • 대괄호 [] - 배열, 중괄호 {} - 각 DTO 객체, 중괄호 안 "name" ~ : 각 객체의 필드와 값
  • 컬렉션을 반환했을 때의 결과와 같이 대괄호[ ](배열)이 첫 시작인 경우 확장할 수 없다.
    => 해당 객체 외에 다른 값을 넣을 수 없다.

 

@GetMapping("/api/v2/members")
public Result memberV2() {
    // 이전 예제와 동일
    
    return new Result(collect.size(), collect);
}

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

//JSON 결과
{
    "count": 3,
    "data": [
        {
            "name": "hello"
        },
        {
            "name": "member1"
        },
        {
            "name": "member2"
        }
    ]
}
  • 컬렉션을 감싸면 유연한 이유? => 확장하기에 유연하다.
    • 컬렉션 사이즈와 같은 값들도 함께 반환해줄 수 있다.
    • 컬렉션을 감싸지 않고 반환하면 JSON 배열 타입으로 전달되기 때문에 확장할 수 없다.

출처: [인프런 김영한 실전 스프링 부트와 JPA 활용 2 - API 개발과 성능 최적화] 

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-API%EA%B0%9C%EB%B0%9C-%EC%84%B1%EB%8A%A5%EC%B5%9C%EC%A0%81%ED%99%94/dashboard

 

실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화 강의 | 김영한 - 인프런

김영한 | 스프링 부트와 JPA를 활용해서 API를 개발합니다. 그리고 JPA 극한의 성능 최적화 방법을 학습할 수 있습니다., 스프링 부트, 실무에서 잘 쓰고 싶다면? 복잡한 문제까지 해결하는 힘을 길

www.inflearn.com