본문 바로가기
Record/boostcourse

프로젝트C. 예약 : 메인, 상세보기 관련 Web API 만들기

by doongjun 2021. 8. 18.

이번 프로젝트는 구현해야할 부분도 많고 난이도도 좀 있어서 처음부터 성공하지 못할 것이라고 생각했는데

다행히 한번에 통과할 수 있었다.

이번 프로젝트를 시작으로 마지막 프로젝트까지 인터넷 예약 관리 시스템을 만들게 된다.

실제 네이버에서 운영하는 예약 시스템과 유사한 서비스이다. 백엔드 파트이기 때문에 Web API를 개발하면 된다.

이번 파트에서는 예약 메인 페이지에서 사용하는 Web API와 예약 메인 페이지에서

전시 상품을 클릭했을 때 보여지는 상세페이지와 관련된 Web API를 개발한다.

 

Comment1
인터페이스는 데이터를 저장할 수 없기 때문에
데이터를 저장할 인스턴스 또는 정적 필드를 선언할 수 없습니다.
대신 상수 필드만 선언할 수 있습니다.
따라서, 인터페이스에 선언된 필드는 모두 상수이므로 public static final의 특성을 갖으며
생략하더라도 컴파일 단계에서 자동으로 붙여지기 때문에 생략해도 무방합니다.
//수정 전
public static final int LIMIT = 4;

//수정 후
int LIMIT = 4;

 

Comment2
해당 메소드를 추상메소드라 하며 인터페이스에 선언된 추상 메소드는 public abstract의 특성을 갖기 때문에 public abstract를 생략하더라도 자동적으로 컴파일 과정에서 붙게 됩니다.
//수정 전
public List<UserComment> getReservationUserComments(Integer productId, Integer start);

//수정 후
List<UserComment> getReservationUserComments(Integer productId, Integer start);

 

Comment3
@RequestParam의 name과 파라미터 명이 같다면 @RequestParam의 name은 생략해도 무방합니다. 또한 defaultValue가 지정되어 있다면 항상 값이 존재하므로 required는 생략해도 무방합니다.
//수정 전
public Map<String, Object> getDisplayInfosByCategoryId(@RequestParam(name="categoryId", defaultValue="0", required=false) int categoryId)

//수정 후
public Map<String, Object> getDisplayInfosByCategoryId(@RequestParam(defaultValue="0") int categoryId)

 

Comment4
@GetMapping 어노테이션의 PathVariable의 이름과 파라미터명이 같다면 PathVariable 어노테이션에서 name을 생략해도 무방합니다.
//수정 전
@GetMapping("/displayinfos/{displayId}")
public Map<String, Object> getDisplayInfosByDisplayId(@PathVariable(name="displayId") int displayId)

//수정 후
@GetMapping("/displayinfos/{displayId}")
public Map<String, Object> getDisplayInfosByDisplayId(@PathVariable int displayId)

 

Comment5
항상 같이 조회될 경우가 많은 로직은 Controller가 아닌 Service단의 메소드에 포함하는 것이 더 좋습니다. 그렇다면 다른 api에서 조회시 동일한 로직을 또 구현해야하기 때문에 코드의 중복이 발생합니다.

 

Comment6
모든 메소드마다 Transactional 어노테이션을 사용하지 않아도 됩니다.

조회시에는 트렌잭션 설정 중 격리수준(Isolation) 설정에 따라 다르지만 기본 설정을 해놨다면 DB의 격리수준 설정에 따른다. MySQL의 경우 격리수준 기본 설정은 "REPEATABLE READ"로 되어 있으며 동일 트랜잭션 내의 데이터의 일괄성을 보장한다.

또한, 해당 Service Method에 트랜잭션을 읽기 외에 Insert, Update, Delete 등을 방지하기 위한 용도가 아니라면

조회시에 Transactional 어노테이션을 사용하지 않는다.

더보기

@Transactional(readOnly = true)가 적용된 메서드에서 @Transactional 혹은 @Transactional(readOnly = false)가 적용된 메서드를 호출 할 경우 무조건 read-only Transaction이 적용된다. 트랙잭션이 전파되는 것은 맞지만 JDBC 벤더들 마다 readOnly속성의 구현이 된 벤더들도 있고 그렇지 않은 벤더들도 있다. 그래서 만약 이때 R을 제외한 CUD를 할 경우 에러를 발생한다. 이것은 참일수도 있고 거짓일 수도 있다. 적용이 되는 경우도 있고 안되는 경우도 있다고 한다.

정상적인 상황인 경우 readOnly는 트랜잭션 범위는 유지하되 조회 기능만 남겨두어 조회 속도가 개선되게끔 하려고 사용한다.

https://pakker.tistory.com/90

 

@Transactional(readOnly = true)

@Transactional(readOnly = true)가 적용된 메서드에서 @Transactional 혹은 @Transactional(readOnly = false)가 적용된 메서드를 호출 할 경우 무조건 read-only Transaction이 적용된다. 트랙잭션이 전파되는..

pakker.tistory.com

주로 Transactional 어노테이션을 사용하는 곳은 다수의 Insert, Update, Delete가 존재하는 Service Method에서 이다.

예를들어 Insert후 Update가 되어야 하는데 Insert는 성공하고 Update가 실패하면 데이터의 무결성이 보장되지 않기 때문에 Update가 실패하면 Insert문으로 실행한 결과가 rollback이 되어야 한다.

 

https://goddaehee.tistory.com/167

 

[Spring] Transactional 정리 및 예제

[Spring] @Transactional 정리 및 예제 안녕하세요. 갓대희 입니다. 이번 포스팅은 [ 스프링 어노테이션 @Transactional  ] 입니다. : ) 들어가기 앞서...... SI를 할때, 현재 어느 쇼핑몰 운영을 맡으며 개..

goddaehee.tistory.com

 

Comment7
SQL의 집계함수를 사용할 때 조건에 해당하는 값이 존재하지 않는다면
집계할 값이 없기 때문에 null을 반환합니다.
또한 해당 쿼리를 실행하는 메소드의 return값이 primitive type인 int형이기 때문에
null을 할당할 수 없습니다.
따라서 해당 쿼리의 결과로 null을 반환할 경우 exception이 발생하게 될 것입니다.

이러한 예외를 처리하기 위해서 try-catch로 예외처리를 하던가 예상되는 예외일 경우 예방해주면된다.

또 다른 방법으로는 MySQL의 IFNULL 함수를 이용하여 null일 경우 지정한 default 값을 반환하도록 해주는 것이다.

SELECT IFNULL(AVG(score), 0) FROM reservation_user_comment WHERE reservation_info_id = 1;

 

Comment8
Inline View 보다는 join으로 해결이 가능하다면 join으로 사용하는 것을 권장드립니다.

만약 필요한 컬럼에 index가 지정되어 있다면 Inline View와 테이블 join시 Inline View의 index를 사용하지 않기 때문에

Inline View에서 조회되는 row가 많을 수록 쿼리의 성능이 좋지 않다.

댓글