Rest api 프로젝트를 하면서 배운 것들을 정리하고자 한다.
JPA entity에서 배운 것들
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Entity @Getter @Setter public class Investments {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(nullable = false) private Long investId;
@Column(nullable = false) private Long userId;
@ManyToOne @JoinColumn(name="id") private Products product;
@Column(nullable = false) private Long investmentAmount;
@Temporal(TemporalType.DATE) private Date investmentDate = new Date();
@Enumerated(EnumType.STRING) private InvestmentStatus investmentStatus = InvestmentStatus.VALID; }
|
JPA와 디비에는 사용하고 싶지 않지만, VO 클래스 멤버 변수로 사용하고 싶은 변수는 @Transient를 붙인다.
이번 프로젝트에서는 h2 디비를 썼다. h2에서는 기본키 자동증가기능을 사용하려면 @GeneratedValue(strategy = GenerationType.IDENTITY) 를 써야 잘 작동했다.
칼럼의 NOT NULL은 @Column(nullable = false) 로 처리했다. (@NotNull은 안됐다.)
@Temporal(TemporalType.DATE) 는 날짜시간 데이터 중 날짜 데이터를 저장한다는 의미다.
시분초는 0으로 처리되어 저장된다.
**@Enumerated(EnumType.STRING)**은 열거형 데이터를 저장할 때 쓰인다.
이번 프로젝트는 열거형 이름 그 자체를 도입했다.
헤더 값 읽기
1 2 3 4
| @GetMapping("/product") public ResponseDto<List<Products>> productGet(@RequestHeader("X-USER-ID") long userId) { ... }
|
@RequestHeader안에 헤더의 키값을 넣어주면 가져올 수 있다!
SpringBoot Could not find acceptable representation(Error: Request failed with status code 406)
1 2 3 4 5 6 7 8 9 10
| @Getter @Setter public class ResponseDto<T> { public ResponseDto(T data, HttpStatus status) { this.status = status; this.data = data; }
HttpStatus status; T data; }
|
406에러는 return된 객체에 setter getter가 없어서 일어났었다.
stream으로 합구하기!
1 2 3 4
| List<Long> numbers = Arrays.asList(1L, 2L, 3L, 4L, 5L);
Long sum1 = numbers.stream().reduce(0L, Long::sum);
|
reduce를 이용하면 구할 수 있었다!!
JSON으로 통신할 때 Object Mapper로 케이스 변환하기
1 2 3 4 5 6 7
| @JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class) public class User { private phoneNumber; @JsonProperty("user_name") private userName }
|
JSON을 반환할 때 ResponseEntity
나는 커스텀해서 ResponseDto를 만들었는데 자바에서는 이미 ResponseEntity가 있더라 크흠…
SQL INIT
어플리케이션을 시작할 때 SQL을 실행해서 스키마를 만들고 데이터를 입력할 수 있다.
1 2 3 4 5
| spring: sql: init: platform: h2 mode: always
|
application.yml에 다음같이 적으면, 항상 어플리케이션을 시작할 때 SQL을 초기화해준다.
Resources 폴더에 schema-플랫폼이름.sql , data-플랫폼이름.sql 을 실행한다.
위 예시는 플랫폼을 h2로 했으니, schema-h2.sql, data-h2.sql이라고 하면된다!!
이때 ddl-auto는 꺼줘여야 잘 작동하게 된다!!
JPA Method이름으로 쿼리 만들기
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public interface InvestmentsRepository extends JpaRepository<Investments, Long> {
default Investments updateInvestStatus(Investments invest, InvestmentStatus status) { invest.setStatus(status); save(invest); return invest; }
List<Investments> findInvestmentsByIdAndStatusEquals(long productId, InvestmentStatus investmentStatus);
List<Investments> findInvestmentsByUserId(long userId);
Investments findInvestmentsByUserIdAndIdAndStatusEquals(long UserId, long productId, InvestmentStatus status); }
|
레퍼지토리에 우리가 원하는 쿼리의 역할을 메서드 이름으로 만들면 그에 해당하는 쿼리를 실행하는 메서드가 만들어진다.
전역 예외 핸들러
1 2 3 4 5 6 7 8
| @ControllerAdvice @RestController public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseDto<String> handleException(Exception e) { return new ResponseDto<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } }
|
예외 핸들러를 따로 만들면, 모든 예외 발생을 다음 메서드로 처리한다.
생성자 주입
1 2 3 4 5 6 7 8 9 10 11
| @RestController @RequestMapping("/api") @RequiredArgsConstructor public class ApiController {
private final ProductsService productsService;
private final InvestmentsService investmentsService; ... }
|
@Autowired 키워드를 쓰는 것보다 @RequiredArgsConstructor와 private final 키워드를 활용한 생성자 주입이 스프링에서 더 권장하는 방법이다.
나도 이 방법을 사용하기 전까지 순환 참조를 하고 있는 줄 모르고 있었는데, 생성자 주입으로 변경하는 과정에서 실수를 확인 할 수 있었다.