
InkBridge 프로젝트를 리팩토링하던 중, 스프링 배치를 적용해야 할 시점이 왔었다.그래서 스프링 배치 공부를 5버전으로 했는데 스프링 부트 3 이상에서만 이 버전을 지원한다는 것을 알았다.원래는 전반적인 리팩토링을 끝낸 뒤 마이그레이션을 진행하려고 했지만, 배치를 5버전으로 공부한 김에 지금 계획했던 마이그레이션을 하기로 했다. 마이그레이션 대상은 다음과 같다.Java 11 -> Java 17Maven -> GradleSpring Boot 2.7 -> Spring Boot 3.4현재 스프링 부트의 가장 최신 버전인 3.4 버전으로 마이그레이션 하기로 했고, 그에 따라 자바 버전도 17이상으로 올려야 했다.그리고 기존 빌드 도구는 Maven이었는데 이 팀 프로젝트를 진행한 이후로는 Gradle만 사용..

기존에 구현한 카테고리 로직에서는 N+1 문제, 재귀적 구조의 한계, 조회 효율성 저하 같은 문제점들이 있었다.그래서 이번 글에서는 카테고리 리팩토링 과정을 담아보도록 하겠다.1. 기존 구조의 문제점1.1 인접 리스트 모델(Adjacency List Model)기존 구조는 인접 리스트 모델로, Category 엔티티 내에 자기 자신을 참조하는 CategoryParent, CategoryChildren 필드를 통해 재귀적으로 자기 자신을 참조했다.1.2 N+1 문제 발생readAllCategory() 메서드에서 부모 카테고리를 조회한 뒤, 각 카테고리의 자식들을 DTO로 변환하는 과정에서 지연 로딩으로 인해 매번 추가 쿼리가 발생했다. 여기서는 @BatchSize설정으로도 최적화할 수 없다.그 이유는 Hi..

책 정보나 작가 정보, 리뷰 등을 등록할 때, 이미지 파일을 함께 저장해야 한다.이미지 파일은 DB와는 다른 IO 작업이라서 어떻게 로직을 구성할지가 중요했다.간단하게 생각하면 "이미지 업로드 → DB 저장" 또는 "DB 저장 → 이미지 업로드" 중 하나를 선택하면 될거라고 생각할 수도 있지만, 트랜잭션 처리, 예외 상황, 서버 부하 등 여러 요소를 함께 고려해야한다. 그래서 이 글에서는 내가 고민한 부분들과 해결 과정을 공유해보겠다.1. 사진 업로드와 DB 저장의 순서에 대한 고민1.1 하나의 요청 안에서 사진 업로드 → DB 저장이 방식은 파일을 먼저 업로드한 뒤, 그 결과로 받은 URL 혹은 파일식별자를 DB에 저장하는 구조다.장점이미지 업로드를 실패하면 DB 작업을 진행할 필요가 없어서 트랜잭션 ..

요청 DTO에서 ConstraintValidator로 Enum 검증하기클라이언트에서 데이터를 넘겨줄 때 @NotBlank, @NotEmpty 등으로 대부분의 유효성 검사를 할 수 있다.하지만 나의 경우에는 Enum에 바인딩 될 수 있는 값인지 검증을 해야했다. 그래서 이번 글에서는 ConstraintValidator를 활용해 Enum에서 지원하는 값인지 검증하는 방법을 공유해보겠다.1. 첫 번째 시도 : 바인딩 이후 도메인 변환 시점에서 Enum 검증처음에는 요청으로 들어온 문자열이 유효한 Enum 값인지 검증할 때, 아래와 같이 도메인 변환 로직 안에서 직접 처리했었다. 예시 Request 클래스DomainStatus.class 응답 이렇게 처리하면 올바르게 예외 처리를 할 수 있다.하지만 이 방식..

주소 도메인의 구조에 문제점이 있다고 판단하여 리팩토링을 진행했다.이 글에서는 ERD와 코드 구조를 개선하고, 어떤 문제를 어떻게 해결했는지 전반적인 리팩토링 과정을 작성해보겠다.1. 리팩토링 내용2개로 나뉘어 있던 테이블 -> 단일 테이블기존 코드 구조 개선 및 기능 추가주소 최대 10개 제한 로직 추가(기존 요구사항에 있던 내용)기본 배송지 설정 로직 추가(개인적인 추가)동시성 문제 해결계층 단위로 나뉘어진 패키지 구조 -> 도메인 기반 패키지 구조추후 모듈 분리를 고려한 구조로 설계2. 기존 ERD 구조 기존 주소 도메인은 general_address, member_address 두 테이블로 분리되어 있었다.아마 아파트 등의 공통 주소를 공유하는 사용자를 고려한 정규화로 추정된다.하지만 이 구조에 ..

1. 기존 방식: RestTemplate으로 업로드1.1 RestTemplate을 이용한 업로드의 문제점기존 InkBridge는 NHN Cloud Object Storage를 사용하여 이미지를 업로드하고 있었고,업로드 방식은 RestTemplate을 이용하여 직접 API 요청을 보내는 방식이었다. 이 방식의 문제점은 다음과 같다.직접 API 호출 필요 → RestTemplate을 사용하여 직접 API를 호출해야 하므로 신경써야할 부분이 많아진다.인증 처리 번거로움 → X-Auth-Token을 수동으로 발급 받아야 하고 토큰 갱신 로직이 필요하다.대용량 파일 확장성 부족 → 현재는 책 사진, 리뷰 사진 등의 작은 이미지만 처리하는데 나중에 동영상 등 대용량 파일을 업로드할 경우 코드의 변화가 클 수 있다...

데이터베이스 접속 정보나 클라우드 관련 키가 퍼블릭한 공간에 노출되면 보안이 취약해진다.특히 클라우드 키가 노출되면 요금 폭탄을 맞을 수도 있다. 따라서 키들을 Github나 DockerHub에 업로드하는 것은 위험하다.이 문제를 해결하는 가장 단순한 방법은 properties/yml 파일을 퍼블릭 공간에 업로드하지 않거나 환경 변수로 처리하는 것이다.하지만 이 프로젝트에서는 NHN Cloud에서 제공하는 Key Manager를 이용하여 설정 키들을 암호화하는 방식을 이용했다.그래서 이 방식을 최대한 살려서 리팩토링을 진행하였다.Key Manager에 대한 설명은 프로젝트를 진행할 때 기록해둔 자료로 대신하겠다.1. 기존 방식KeyConfig.classKeyStore메서드로 복호화를 진행하는 코드이며, ..

NHN Academy에서 진행했던 InkBridge 프로젝트는 DBCP2를 커넥션 풀로 사용하고 있다. 그러나 현재 스프링부트의 기본 커넥션 풀은 HikariCP를 채택하고 있다.이 부분에 의문이 들어서 두 커넥션풀에 대해서 공부해보고 나서 HikariCP로 전환하기로 결정하였다.이 글에서는 그 과정에 대해서 설명해보겠다.1. DBCP2DBCP는 Apache Commons 프로젝트에서 제공하는 커넥션 풀이다. DBCP는 오랜 기간 동안 사용되었으며, 많은 레거시 시스템에서 사용되고 있다.2002년에 DBCP1이 출시되었지만 1.x버전대에서는 성능이 부족하고 멀티스레드 환경에서 동기화 이슈가 종종 발생했다고 한다.DBCP2는 DBCP1의 후속 버전으로 2014년에 출시되어 성능 개선 및 버그 수정이 이루어..

개발 중 CSS가 어떻게 적용되는지 확인하기 위해 크롬 개발자도구(DevTools)를 열었는데 의도한 CSS가 적용되지 않는 문제가 생겼다.처음 페이지를 로드할 때는 제대로 보였지만, 개발자도구를 열기만 하면 이전의 CSS로 적용이 되는 현상이 발생했다.사파리나 다른 브라우저에서는 아무런 문제가 없었다.이 문제를 해결하는 과정에서 Chrome DevTools의 Override 기능을 알게 되었고, 이를 활용하는 방법까지 정리해 보았다.1. 이상한 점 발견 : "style.css"만 문제?내 프로젝트에서는 Tailwind를 사용 중이었고, 빌드 후에는 Tailwind CSS는 일반 CSS로 변환된다.(이 부분은 Tailwind CSS 사용하기(3 버전)에 정리해두었다.)처음엔 Tailwind의 빌드 문제를..

ServletContext의 Attribute에서 List 값을 다운 캐스팅해서 가져올 때 Unchecked Warning이 발생했는데 이 경고를 해결하는 두 가지 방법을 소개해보겠다.1. Unchecked Warning 원인1.1 제네릭과 타입 소거Unchecked Warning은 주로 제네릭과 관련해서 발생한다. 제네릭은 컴파일 타임에만 타입을 유지하고 런타임에는 타입이 소거된다.이로 인해 컴파일 타임에는 타입 안전성을 보장하더라도 런타임에서는 이를 보장하지 못하게 된다.1.2 주요 원인은 Object로 저장해서 제네릭 타입으로 다운 캐스팅을 해야할 때반환 타입이 Object이므로 특정 타입으로 캐스팅할 때 컴파일러는 타입 안전성을 검증할 수 없다. 왜냐하면 런타임에는 제네릭이 소거되기 때문이다.아래..

옛날에 NHN Academy 부트캠프에서 Servlet과 JSP로 쇼핑몰을 만드는 프로젝트를 진행했었다.당시에는 주어진 TODO를 채우면서 Servlet을 설정하고 FrontController의 형태를 구현하는 방식으로 프로젝트를 진행했다.하지만 여러 이유로 완성하지는 못했고, 이후에 완성하려 했지만 관련 자료를 잃어버려서 하지 못했다. 그래서 공부했던 기록을 다시 남겨둘 겸 그 프로젝트를 다시 시작하려고 한다.과거의 아쉬움을 채우는 데 그치지 않고 더 발전된 방식으로 만들어보는 것이 목표다.이번에는 나만의 방식으로 조금 더 스프링의 DispatcherServlet의 형태를 만들어 볼 것이고 여러 방면에서 스프링의 철학을 담아보려고 한다.(이미 진행중이라서 느끼는건데 스프링을 모방하는 것은 쉽지 않고, ..