Back-end

Back-end/Spring Boot

@ConfigurationProperites 이용한 프로퍼티 객체 관리

What? 이전에는 @Value 를 사용해 *.yml , *.properties 의 프로퍼티 값들을 가져왔습니다. 개인적으로는 기능에 문제가 없었기 때문에 개선의 필요성을 인지하지 못하고 있었는데, 지인분의 추천으로 @ConfigurationProperties 과 @ConstructorBinding 어노테이션을 알게 되어 공유합니다. Why? 기존의 문제점부터 짚고 넘어가겠습니다. 아래와 같은 yml이 있다고 가정합니다. server: port: 8080 domain: http://localhost:8080 동적 타입 문제 @Value("${server.port}") private String stringPort; @Value("${server.port}") private Integer integerPo..

Back-end/트러블 슈팅

[서버 트러블 슈팅] EC2 의문사 해결 과정 (진행중)

💡 발단 아침에 일어나자마자 섬뜩한 슬랙 알림을 보게됐다. EC2 CloudWatch를 확인해보니 CPU 사용률이 99%를 찍고 사실상 중지된 모습. DB 커넥션은 급락했다. 서버가 정상 작동하지 않는다는 간접적인 신호인 것으로 파악했다. 원인 파악 시나리오 1 : 누군가의 무분별한 요청 ❌ 몇주동안 잘 작동하고 있었기 때문에 외부 변수부터 찾으려고 했다. CloudWatch에서 서버 요청 로그를 확인했다. 무분별한 해외 IP 요청 때문에 스레드 풀이 초과됐다는 레퍼런스를 봤었기 때문이다. https://mopil.tistory.com/128 아래는 서버가 죽을 당시의 로그 기록. 확인해보니 헬스 체크 컨트롤러로 계속해서 요청이 들어오고 있었다. 그런데 이게 의문사의 원인이라고는 생각하지 않는다. 해외 ..

Back-end/Spring Boot

[Spring Boot] logback으로 CloudWatch에 서버 요청 로깅하기

💡 발단 앱을 런칭하기 전부터 불안한 점이 있었다. "만약 서버가 터진다면... 어떻게 해결하지?". 그래서 생각한게 로깅이었다. 로깅이 모든 문제를 해결할 수는 없겠지만, 문제를 해결하는데 필요한 결정적인 실마리를 줄 수 있다고 생각했다. 적어도 비정상적인 요청 혹은 서버내의 에러를 파악할 수 있다고 생각해서 바로 적용하게 됐다. 물론 서버에 SSH 접속해서 직접 로그를 확인하는 방법도 있다. 그래서 해당 프로젝트에선 docker를 사용해서, docker log 명령어를 사용해서 확인해왔다. 그러나 만약 서버가 죽는다면? SSH 접속 자체가 안된다면 원인을 파악할 방법이 없어지게 된다. 외부의 어딘가에 로그들을 저장해둘 필요가 있었다. 그래서 찾아본게 AWS CloudWatch. 공식적으로 로그 기능을..

Back-end/Spring Boot

[Spring JPA] 페이지네이션 일대다 FETCH JOIN 문제와 default_batch_size

💡발단 HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory WARN이 반복적으로 나오는 걸 보게 됐다. 이때까지만 해도 ERROR은 아니니까 적당히 나중에 리팩토링해야겠다고 생각했다. 그러나 쿼리문을 자세히 보니 큰 문제가 있다는걸 깨달았다. 실제 쿼리를 날릴 때 limit 을 걸지 않는다. 알고보니 DB에서 모든 row를 가져온 뒤에 메모리상에 올려두고 처리하는 방식이었다. 아래는 Querydsl 코드. location_bookmark는 location에 대한 일대다 엔티티이다. location_bookmark를 레프트 조인하고 fetch join하면서 발생하는 부분이 핵심이다. public JPA..

Back-end/Spring Boot

[Spring JPA] 엔티티 컬렉션 필드를 초기화해야 하는 이유

💡 발단 발단 부분은 Java적인 문제이고, 본론은 이 다음에 나오니 건너뛰시길 추천드립니다. 프로젝트를 진행하다가 엔티티 생성자쪽에서 에러가 발생했다. Builder 생성자에서 엔티티 컬렉션 필드를 할당하는 쪽에서 생긴 문제인데, 너무 당연하게 생각했어서 더 많은 생각을 하게 됐다. Spring 프로젝트를 2번 하는 동안 컬렉션 필드를 생성자에서 만들어본 적이 없었던 것. 상황은 이렇다. 컬렉션 필드를 addAll()로 할당하는 부분에서 NPE(Null Pointer Exception)가 발생한 것. 문제는 NULL값이 들어갈 때였다. // ... 필드 private List feedImages = new ArrayList(); @Builder private Feed(String title, Strin..

Back-end/Spring Boot

[Spring Boot] 커서 페이징(no offset)에서 Page 대신 Slice 사용하기

💡발단 우리 프로젝트의 프론트엔드는 모바일 안드로이드로, 대부분의 조회 페이지가 무한 스크롤로 구현된다. 즉, 오프셋이 쓰이지 않고 커서를 이용해 페이징한다. 마지막으로 응답했던 레코드의 id 값을 커서(커서 아이디)로 사용한다. 맨 처음엔 커서 아이디를 0으로 요청 -> 아이디 1~10을 갖는 레코드를 응답하고, 그 다음엔 커서 아이디를 10으로 요청 -> 아이디 11~20 을 갖는 레코드를 응답하는 식이다. 이 상황에서 조회 기능들을 구현하면서 배우게 된 Page의 단점과 Slice의 장점을 서술하고자 한다. ❗Page, Pageable의 대부분의 필드는 커서 페이징에 필요하지 않다. 애초에 커서 페이징에는 offset이 쓰이지 않는다. offset이 없으므로 getOffset, getPageNumb..

Back-end/협업

[백엔드의 협업] 기획 단계의 내용들을 개발 초기에 상수화하기

기획 단계 혹은 요구사항 분석 단계에서 API 명세, 와이어프레임 작업, 컨벤션 확립 등의 과정을 거친다. 예를 들어 회원 이름의 글자 수 제한은 기획 단계에서 정해진다. 이러한 값들을 초기에 상수화하여 관리하면 관심사를 분리할 수 있고 개발이 편해진다. 추후 기획이 변경되더라도 다른 파일들의 git 변경 이력을 최소화할 수 있다는 장점도 있다. 와이어프레임의 정보를 상수화하기 아래 사진은 피그마로 디자인된 게시글 작성 페이지. 극초기의 와이어프레임이었다. public final class ConstraintConstants { public static final int SPOT_IMAGE_MAX_SIZE = 5; public static final int SPOT_CONTENT_MAX_LENGTH = ..

Back-end/Spring Boot

[Spring JPA] Hibernate에서 지원하지 않는 MySQL 랜덤 함수 직접 만들기 + Expressions 파헤쳐보기

상황 설명 현재 프로젝트에는 환경 퀴즈를 푸는 서비스가 있고, 유저가 제출한 퀴즈를 복습하는 기능이 있다. 특이한 점은 복습 퀴즈들을 랜덤으로 보여줘야 한다는 것. 즉 문제들의 정렬 방식을 랜덤으로 두어서 유저가 요청할 때마다 다른 퀴즈를 봐야한다. 발단 문제는 랜덤 정렬을 구현하면서 발생했다. @Override public Slice findRecentQuizzes(Long userId, int limit) { JPAQuery query = queryFactory.selectFrom(quiz) .where(...) .orderBy(NumberExpression.random().asc()) // 이 부분 // ... } 발생한 에러: org.springframework.dao.InvalidDataAcce..

Back-end/Spring Boot

[Spring Boot] 개발 환경 분리와 ddl-auto 재앙 방지 + @Profile

💡발단 현재 프로젝트에선 기존에 application.yml 설정 파일 하나에 모든 설정 정보를 넣어놓고 있었다. 가장 큰 문제는 로컬에서 적용하던 설정을 실제 배포된 인스턴스에 적용하는 휴먼 에러가 발생할 수 있다는 것. 그리고 가장 무서운건 ddl-auto의 존재다. 유명한 개발바닥 재난급 장애 영상 만약 실서비스에서 ddl-auto : create로 실행을 한다면... DB가 날아가고 복구하는데 너무 큰 비용이 생기게 된다. 그 외에도 개발 환경을 분리하면 환경별로 설정을 관리할 수 있게 되기 때문에 편리한 부분이 많다. 스프링은 공식적으로 profile(프로파일) 기능을 지원한다. 🛠 프로파일 적용 적용 방법은 간단하다. 인텔리제이에서는 서버 Edit Configuration -> Active P..

Back-end/Spring Boot

[Spring Boot] @Valid 유효성 검사 (jakarta validation)

💡 유효성 검사가 필요한 이유 1. 컨트롤러 레이어에서 Request Body 검사를 함으로써 API 명세를 정확히 지킬 수 있음 2. 프론트엔드 개발자의 개발중 실수를 즉각적으로 알려줌 3. 비즈니스 로직상 올바르지 않은 형태의 데이터가 DB에 저장되는 것을 방지함 사용 방법 의존성 추가 (Spring Boot 3.0, Java 17) dependencies { // ... implementation 'org.springframework.boot:spring-boot-starter-validation:3.0.5' validation 종류 validation 종류 참고 : https://jeongkyun-it.tistory.com/130 개인적으로 주로 사용하는 어노테이션의 종류와 사용 팁은 다음과 같다..

zorbathegeek
'Back-end' 카테고리의 글 목록