스프링부트 15

데이터 유실 방지 - 외부 저장소(S3) 장애 대응

프로젝트를 진행하던 중, 외부 저장소 ( S3 ) 를 변경해야 하는 상황이 발생하였고 변경까지 오랜 시간이 소요되는 것이 아니었기에 서버를 실행 시켜둔 상태로 기존 S3 를 종료하고 새로운 S3를 반영하던 중, 회원 등록 API가 호출되며 사진 데이터가 유실되는 문제가 발생하였다.모든 프로젝트가 그러하겠지만, 해당 프로젝트는 실제 운영 중인 매장에서 사용하기 위해 개발 중인 만큼 데이터가 유실 되지 않는 것이 중요한 포인트였기에 대안을 생각하기로 하였다.현재 회원 등록 API의 요청 값은, 이름/성별/나이 그리고 이미지 URL 의 Json 데이터를 담은 req와 이미지 파일을 담은MultipartFile 형식의 file을 함께 전달 받고 있다. public ApiResult addCustomer( ..

기술적 고민 2025.03.04

Content-Type 'application/octet-stream' is not supported 원인 및 해결 방법

[2025-02-03 22:48:44.371] [http-nio-8080-exec-8] [8706cf15-ff91-49be-98c7-236cb49d64c6] ERROR [.error.GlobalExceptionHandler.handleException:25 ] - Content-Type 'application/octet-stream' is not supported[2025-02-03 22:48:44.372] [http-nio-8080-exec-8] [8706cf15-ff91-49be-98c7-236cb49d64c6] ERROR [.error.GlobalExceptionHandler.handleException:26 ] - Exception : org.springframework.web.HttpMediaT..

Springboot 2025.02.05

Exception Handler ( 커스텀 익셉션 ) 사용 이유 및 작성 방법

코드를 작성하다보면 에러를 발견하는 것은 너무나 당연한 수순이다.이러한 에러를 발견했을 때 이를 해결하기 위해 로그를 확인하고, 문제가 되는 부분을 추적해 해결해야만 한다.하지만, 로그를 한 번이라도 확인해봤으면 알겠지만 이러한 로그는 길이가 짧지 않아 이를 읽고 파악하는데 생각보다 많은 시간이 소요되게 된다.또한, 이러한 로그는 보안상 local 환경에서만 확인할 수 있도록 하여 별도의 처리가 없는 한 API를 가져다 쓰는 프론트 쪽에서는 오류가 발생해도 어떤 것이 문제인지 파악할 수 없는 경우가 빈번히 발생하게 된다.예시를 들어보겠다.아래 코드는 Integer a 를 전달 받고, 해당 값에 +3 을 더한 값을 반환하는 코드이다. @PostMapping("/post") public int po..

Springboot 2024.11.25

메타데이터를 이용한 객체 값 자동 생성 ( DatabaseMetaData )

테스트 코드 작성 중 객체 내부의 값을 랜덤으로 생성해주는 과정이 반복되어 이를 해결하고자 FixtureMonkey를 사용하게 되었다.다만 FixtureMonkey를 사용하기 위해서는 엔티티 내부에 어노테이션으로 @Size 등의 제약사항을 작성해주어야 했는데, 작성 중인 프로젝트에서는 Mybatis를 사용해 코드를 작성하는 만큼 @Size와 같은 어노테이션을 오직 FixtureMonkey 만을 위해 엔티티에 작성해야할 이유가 없다고 느껴졌다.또한 테스트 코드를 작성을 위해 기존 코드에 영향을 주는 것은 좋지 않다 판단하였기에, Mybatis에서 별도의 설정 없이 랜덤한 값을 생성할 수 있도록 직접 코드를 작성하기로 하였다.코드를 작성하기 전 중요하게 생각한 포인트는 아래와 같았다.기존 코드에 영향이 가지..

기술적 고민 2024.10.08

스프링부트 Repository 커스텀 (분리)

프로젝트의 규모가 크지 않거나, 시작 단계일 경우 하나의 Repository에서 모든 쿼리를 관리하는 경우를 종종 볼 수 있을 것이다.public interface MemberRepository extends JpaRepository { Optional findByEmailAndNickname(String email, String nickname); List findByRole(Role role); ... 등등}위의 코드는 아직 정의된 메소드가 두 가지 밖에 없기에 잘 와닿지 않겠지만, 관련 쿼리가 몇 십,백개가 넘어가거나현재는 Jpa만 사용하지만 jpsql 혹은 querydsl 등으로 직접 쿼리문을 작성하는 경우 하나에 파일에서 관리하게 되면 코드가 복잡해져 유지보수에 있어 어려움을 느낄 ..

Springboot 2024.09.23

Springboot 실행과 동시에 종료 오류 해결

https://start.spring.io/ 에서 생성된 프로젝트를 그대로 실행하였을 경우 springboot가 실행과 동시에 종료가 된다.다양한 이유가 있겠지만, 로그에 Tomcat 관련 내역이 없다면 의존성을 추가하지 않아 발생하는 오류일 가능성이 높다.해결 방법 → 의존성 추가implementation 'org.springframework.boot:spring-boot-starter-web'자칫 org.springframework.boot:spring-boot-starter 과 동일한 의존성인 줄 아는 분들도 계시지만, 다른 의존성이기에 별개로 추가해주어야 한다.

Springboot 2024.09.04

API 응답 공통 포맷 ( Spring / Java )

응답 공통 포맷은 협업에 있어 일관성 있는 형식으로 값을 제공함으로 효율적인 작업을 위해 설정하곤 한다.이러한 포맷은 어디까지나 편의를 위해 설정하는 만큼 서비스에 따라 설정 값은 다르지만,응답 상태 값 ( error code , message 등의 상태를 확인할 수 있는 값 )반환 데이터 값위 두 가지는 많은 곳에서 활용하는 것같다.아래 코드는 기본적인 코드이다.@Getterpublic class ApiResult { private final String code; private final String message; private final T data; public ApiResult(String code, String message, T data) { this.code = code; ..

Springboot 2024.09.02

로그 구분 ID 추가 logback-spring.xml + Filter

코드를 작성하고 테스트를 하거나 디버깅을 하는 과정에서 로그를 봐야하는 상황은 정말 많이 존재한다.하지만 이러한 로그에는 구분을 위한 ID 값이 존재하지 않기에 특정 로그를 찾는데 어렵다는 문제점이 존재하였다.이러한 사항 과정의 불편함을 줄이기 위해 각 로그 별로 특정 ID를 생성해 함께 출력하도록 하려 한다.로그에 ID 값을 출력하기 위해서는 로그 포맷을 변경해주어야 하는데, 기본 설정의 경우 아래와 같은 형식으로 로그가 출력되고 있을 것이다. 포맷 변경을 위해서는 application.yml 에서 간단하게 처리할 수 있으나, 세부적인 설정이 불가하기에 이번에는 logback-spring.xml 을 통해 변경해보도록 하겠다.logback-spring.xml 파일은 resources 아래에 생성해주면 된..

Log 2024.08.12

개발 환경 나누는 방법 ( application , local, dev )

서버를 두고 작업하는 경우 보통 로컬에서 사용하는 환경과, 서버에서 사용하는 환경을 분리해서 사용하는 경우가 있을 것이다.대표적으로 로컬에서는 h2를, 서버에서는 mysql과 같이 사용을 하거나, mysql을 사용하더라도 로컬에서는 localhost 를, 서버는 주소를 입력하여 사용하곤 할텐데 이를 간단하게 구분할 수 있는 방법이 있다.이는 application를 여러개 만들어 사용하는 방법인데, 보통은 아래와 같이 생성하여 사용하지만 필요한 만큼 생성하고 제거하면 된다.application , application-local , application-dev , application-prodapplication.yml 에는 어떤 환경에서든 공통적으로 사용하는 사항들 ( 스웨거, 변수,, 등등 )local..

Springboot 2024.08.10

Filter 로그 처리 후 Controller에 요청 전달하기

이번 글에서 활용할 Filter 코드는 아래와 같다.( 해당 코드에 대한 설명과 과정은 아래 글을 참고 )https://taekt.tistory.com/12@Slf4j@Componentpublic class LogFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ContentCachingRequestWrapper requestWrapper = new ContentCaching..

Log 2024.08.08