블로그

REST API

REST API의 의미REpresentational State Transfer Application Programming Interface특정 시점의 [자원] [상태] [전달]stateless - 어느 시점, 상태더라도 상호 소통이 가능 REST API의 특징자원 - URIURI는 자원(명사)을 표현해야한다. 띄어쓰기는 하이픈으로 표현행위 - Http Method행위는 Http Method로 표현 (GET, POST, PUT(갈아끼기), PATCH(부분 수정), DELETE)표현 - JSON자원의 표현은 주로 JSON(key, value) 형식으로 전달된다.웹에서는 일련의 문자열(byte)로 직렬화하여 송수신하고, 역직렬화(객체화) 과정을 통해 데이터를 사용한다. RestController@RestController = @Controller + @ResponseBody json 형태로 응답한다.- new ResponseEntity(T, HttpStatus) 반환- Custom Response 객체 반환@RequestBody로 json 요청 객체를 받는다.  Ajax 사용법type에는 GET, POST, PATCH, DELETE 등 HTTP METHOD가 들어간다.url - GET 요청 시에는 파라미터와 쿼리스트링을 문자열에 붙여 전달 가능하다.data는 요청 시 필요한 객체를 전달한다.GET 방식이면 url뒤에 쿼리스트링으로 붙어서 전달된다. POST 방식이면 JSON.stringify()로 형태 변환이 필요하다.contentType은 요청하는 데이터의 형태이다.  json 형태는 application/json; charset=utf-8form 형태는 applicaiton/x-www-form-urlencoded; charset=utf-8 (기본값)multipart/form-data 형태는 contentType: false, processData: false로 지정한 후enctype: "multipart/form-data"로 설정한다.dataType은 응답받는 데이터의 형태이다.기본값은 text이며, json 형태일 경우 json으로 설정해주면 된다. $.ajax({ type: "GET", url: `/api/board/${id}`, dataType: "json", success: function(result){ // 반환된 객체 처리 // 객체 상태에 따라 성공, 에러 분기 처리 } }); $.ajax({ type: "POST", url: "/api/board", data: JSON.stringify(data), contentType: "application/json; charset=utf8", dataType: "json", success: function(result){ location.href=`/board/detail/${result.body}`; // result.body == entity의 id // 서버에서 return id하고 dataType text로 받으면 result == id }, error: function(error){ // http status를 200으로 보내지 않으면 해당 분기 타게 됨 // 반환 객체는 responseText에 text로 담기기 때문에 JSON으로 변환 시켜줌 let errRes = JSON.parse(error.responseText); alert(errRes.body); } });$.ajax({ type: "POST", url: "/api/board", data: new FormData($("form")[0]), enctype: "multipart/form-data", contentType: false, processData: false, dataType: "json", success: function(result){ } }); 

REST_API

실전! 스프링 부트와 JPA 활용2 (API 개발과 성능 최적화) - 김영한

 Rest API를 구현하며 알아보는 Entity를 반환하는 6가지 방식   Version 1. 엔티티를 직접 노출하는 방식 [문제] 엔티티를 직접 반환하는 방식은 중요한 정보가 노출되거나 필요하지 않은 데이터까지 불러 데이터 구조가 비대해질 수 있다.연관관계까지 호출할 경우, 역시 모든 속성을 호출함으로 비대해진다. 양방향 연관관계일 경우 한 쪽 컬럼에 @JsonIgnore을 설정하여 무한루프가 빠지지 않도록 주의해야한다.연관관계는 fetch option을 lazy로 설정하면 호출되지 않기 때문에 lazy로 설정한 후 필요한 경우 속성을 호출하는 방식으로 사용하는 것을 권장한다.   Version 2. 엔티티를 DTO로 변환하는 방식 stream을 이용한 entity -> dto 변환stream에서 filter, map을 사용해 중간변환을 하고 find 또는 collection 형태로 최종 가공. 필요한 속성 중심으로 가공할 수 있어 깔끔하게 전달 가능 [문제] 여전히 쿼리 반환 시 모든 데이터가 호출되기 때문에 성능 상 문제가 생길 수 있음   Version 3. 엔티티를 DTO로 변환하며 페치 조인으로 최적화 jpql에서는 join fetchquerydsl에서는 join() 후 fetchJoin() fetch join을 이용하여 한 번의 쿼리로 연관관계까지 모두 호출하도록 한다.그렇게 되면 지연 호출(lazy)로 인한 추후 연관관계 호출 시 추가적인 쿼리 발생이 일어나지 않으며,크로스 조인(cross join)으로 인한 데이터 중복 호출도 발생하지 않는다. 일대다 컬렉션 조인이 있을 경우, select distinct로 데이터 뻥튀기를 해결해줘야 한다. [문제] fetch join의 경우, 일대다 컬렉션 연관관계는 한 개 밖에 사용하지 못한다는 한계가 있다. 또한 컬렉션 페치 조인을 사용하면 페이징이 불가능하다.모든 데이터를 읽어온 후, 메모리에서 페이징을 한다;;   Version 3.1. 컬렉션 페치 조인 - 페이징 한계 돌파 일대다 관계에서 페이징을 하려면 일(1)을 기준으로 해야되지만컬렉션 페치 조인을 사용할 경우 다(N)이 기준이 되어 row가 생성된다.-> 하이버네이트는 이러한 문제로 메모리에서 페이징을 진행한다; [해결] ToOne 연관관계는 fetch join으로 호출한다. ToMany 컬렉션 연관관계는 지연로딩으로 조회한다. 이 때, hibernate.default_batch_fetch_size 또는 @BatchSize을 이용한다.이 옵션은 컬렉션 또는 프록시 객체를 설정한 size만큼 in query로 조회한다.1 + N 쿼리에서 1 + 1 쿼리로 최적화 된다.   Version 4. JPA에서 DTO 직접 조회 특정 용도에 필요한 컬럼들로만 구성한 DTO로 조회 jpql의 경우, select new package명.dto.ResponseDto(컬럼...) 및 클래스 명시querydsl의 경우, select(QueryProjections.constructor(Dto.class, 컬럼...)) 역시, 컬렉션 조회 시 1+N 문제가 있고, 해당 문제는 아래에서 다룸   Version 5. JPA에서 DTO 직접 조회 - 컬렉션 조회 최적화 컬렉션 연관관계는 일(1)의 id 목록을 파라미터로 전달하여 in query 별도 조회stream groupingBy로 일(1)의 id로 다(N)를 묶은 후 -> 일(1)의 속성에 넣어줌 /** order과 order_item으로 살펴보는 예시 */ // 1. order 목록 호출 List<OrderQueryDto> result = findOrders(); // 2. order의 id 추출 List<Long> orderIds = result.stream() .map(o -> o.getOrderId()) .collect(Collectors.toList()); // 3. order id로 order item 호출 List<OrderItemQueryDto> orderItems = em.createQuery( "select new ...OrderItemQueryDto(컬럼...)" + " from OrderItem oi" + " join oi.item i" + // 참고로, fetch join은 엔티티 조회에서만 가능 " where oi.order.id in :orderIds", OrderItemQueryDto.class) .setParameter("orderIds", orderIds) .getResultList(); // 4. order item을 order id로 grouping Map<Long, List<OrderItemQueryDto>> orderItemMap = orderItems.stream() .collect(Collectors.groupingBy(orderItemQueryDto -> orderItemQueryDto.getOrderId())); // 5. order에 order item 매핑해주기 result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));   Version 6. JPA에서 DTO로 직접 조회 - 플랫 데이터 최적화 쿼리 한 번으로 모든 데이터 조회 [문제] 조인으로 인해 중복 데이터 생성될 수 있다.쿼리에서는 데이터를 모두 호출하기 때문에 애플리케이션에서 추가 작업이 발생할 수 있다.페이징이 불가능하다.

강의김영한REST_APIDTO

채널톡 아이콘