최신 글
-
웹 기반의 DB 형상 관리 시스템 구축기(feat. Flyway)
프로젝트 배경현재 내가 속한 팀은 여러 개의 스쿼드로 나뉘어 운영되고 있다. 스쿼드의 개수가 꽤나 많은데 이 중 4개 이상의 스쿼드가 동일한 공통 데이터베이스를 공유해서 사용하고 있다. 그리고 배포환경도 일반적인 회사들과 비슷하게 여러 환경으로 나누어져 있다.데이터베이스 쿼리는 아래와 같이 Git을 통해 형상관리되고 있는데, 각 스쿼드는 스프린트 단위로 개발을 진행하면서 필요한 SQL 파일을 생성하고 이를 프로젝트의 changelog 폴더에서 버전별로 관리한다. 그리고 상위 환경으로 배포할 때는 해당 버전에 포함된 쿼리들을 개발자가 수동으로 실행하여 반영하고 있다.sql/├── changelog/│ ├── A/ # A 스쿼드 │ │ ├── v5.7.1..
2025.01.19
-
Spring 트랜잭션은 언제 어떻게 롤백 될까? -2편
개요이전 글에서는 Spring의 @Transactional 어노테이션이 기본적으로 어떻게 동작하는지, 그리고 예외 처리에 따라 트랜잭션이 롤백되는 메커니즘에 대해 다루었다. 이번 글에서는 실제 코드와 테스트를 통해 다양한 상황에서 트랜잭션이 어떻게 동작하는지 살펴볼 예정이다. 글을 읽는 분들도 각 상황에서 결과를 예측해 보며 따라가면 이해에 도움이 될 것 같다.동일한 클래스 내에서의 트랜잭션 동작본인 메서드에서 예외 발생 시 롤백다른 메서드 호출 후 예외 발생 시 롤백본인 메서드에서 try-catch로 예외 처리 후 커밋다른 메서드 호출 후 본인 메서드에서 try-catch로 예외 처리 후 커밋본인 메서드에서 noRollbackFor 설정 후 예외 발생 시 커밋noRollbackFor 설정된 다른 메서드..
2024.12.22
-
Spring 트랜잭션은 언제 어떻게 롤백 될까? -1편
개요스프링에서 @Transactional 어노테이션을 사용하면 트랜잭션이 자동으로 관리된다. 로직이 성공적으로 처리되면 commit을 통해 데이터베이스에 변경 사항이 반영되고, 오류가 발생하면 rollback을 통해 모든 변경 사항이 취소된다. 다만 이는 기본적인 개념으로 이번 글에서는 더 나아가 세부적으로 롤백이 정확히 언제 발생하고(1편), 특정 상황을 예시로 해당 로직이 롤백이 될지 안 될지에 대해 맞춰보는 식(2편)으로 알아보고자 한다. Check Exception, Unchecked Exception먼저 자바에서는 예외를 크게 다음과 같이 Exception과 Error 두 가지로 나누고 있다.Exception은 입력 값에 대한 처리가 불가능하거나 프로그램 실행 중에 참조된 값이 잘못된 경우인 ..
2024.10.04
-
[gradle] implementation, api 차이
개요Gradle에서 의존성을 설정할 때는 compileOnly, runtimeOnly, implementation, api 등 여러 가지 옵션을 사용할 수 있다. 특히 api와 implementation은 외부 라이브러리를 프로젝트에 추가할 때 가장 자주 사용하는 두 가지 방법으로, 이 둘은 모두 라이브러리를 추가하는 기능을 제공하지만 각기 다른 방식으로 동작하기 때문에 차이점을 알아두면 좋다.dependencies { api 'org.apache.httpcomponents:httpclient:4.5.7' implementation 'org.apache.commons:commons-lang3:3.5'}참고로 이전 Gradle 버전에서는 implementation 키워드가 없었고, 대신 comp..
2024.09.07
-
Spring Boot에서 여러 Kafka 클러스터 사용하기
개요프로젝트에서 일반적으로 하나의 Kafka 클러스터만을 사용하는 경우가 대부분이지만, 경우에 따라 서로 다른 환경의 Kafka 클러스터에 동시에 연결해야 할 필요가 생길 수 있다.나는 이번에 작업하면서 기존의 연결된 Kafka 클러스터 말고도 또 하나의 새로운 클러스터를 연결해야 했는데, 작업을 진행하면서 얻었던 지식을 공유하고자 작성하게 되었다. 컨슈머 설정Spring Boot는 기본적으로 ConcurrentKafkaListenerContainerFactory를 자동으로 구성하여 Kafka 리스너 컨테이너를 생성한다. 그러나 여러 Kafka 클러스터에 연결하려면 각 클러스터에 대해 별도의 ConcurrentKafkaListenerContainerFactory 빈을 생성하고, 해당 클러스터에 맞는 C..
2024.08.17
-
[JPA] deleteAll(), deleteAllInBatch(), deleteInBatch() 정리
개요Hibernate(JPA)에서는 레코드를 삭제할 수 있는 아래의 다양한 메서드들을 지원한다.delete(),deleteById()deleteAll(), deleteAllById()deleteInBatch()deleteAllInBatch(), deleteAllByIdInBatch()다양한 메서드를 제공하는 것은 좋지만, 네이밍만 보고 혼란스러운 것들이 몇몇 있다. 대표적으로 deleteInBatch()와 deleteAllInBatch(), 그리고 deleteAll()과 deleteAllInBatch()와 같은 메서드들이다.이번 게시글에서는 위 메서드들이 실제로 어떻게 삭제를 처리하는지를 살펴볼 것이고, 연관관계가 없는 경우와 연관관계가 있는 경우로 나누어 삭제 동작을 분석해 볼 것이다. 사전 세팅DB..
2024.08.12
-
단방향 @OneToMany의 문제점
개요JPA를 학습하다 보면 @OneToMany를 사용할 때 단방향보다는 양방향 매핑을 사용하라는 말을 자주 듣게 된다.왜 그런지 이해하기 위해 단방향 @OneToMany를 사용하는 두 가지 방법을 사용하여 문제점에 대해 살펴보고, 양방향을 사용했을 때 어떤 점이 개선되는지 알아보자매핑 테이블을 사용한 단방향 @OneToMany 동작 과정@JoinColumn 사용한 단방향 @OneToMany 동작 과정양방향 @OneToMany를 사용했을 때의 동작 과정 1. 매핑 테이블 사용매핑 테이블을 이용한 @OneToMany 엔티티 설계 예시는 다음과 같다.@Entity@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)public class Book { @I..
2024.07.26
-
NextStep DDD 세레나데 수료 후기
계기4.30 ~ 6.10까지 DDD 세레나데 6기 과정에 참가했다. 요 과정을 듣게 된 이유는넥스트스텝 강의 중 듣고 싶었던 강의 중 하나였기도 하고,기수별로 거의 일 년에 한 번 열리기에 2년 차인 지금이 적기라 생각했고,DDD관련해서는 책으로 이론공부만 했었는데 실습을 해보고 싶었고,현재 프로젝트에서 이 구조를 기반으로 하고 있기 때문에 업무에 도움이 될 거라 생각했기 때문이다. 커리큘럼강의 커리큘럼은 다음과 같다. 1주차 - 도메인 주도 설계 이해첫 주는 도메인 주도 설계의 등장 배경에 대해서 학습했는데, '도메인', '모델', '도메인 모델', '도메인 주도 설계'라는 단어의 정의에 대해 생각해 보는 시간을 많이 가졌다. 미션에서는 2단계와 3단계가 개인적으로 많은 도움이 되었다.0단계 - JUn..
2024.07.15
-
@TransactionalEventListener 사용 시 주의점
문제 상황@Service@RequiredArgsConstructorpublic class CurrentValueChangeService { private final ExampleRepository exampleRepository; private final ApplicationEventPublisher applicationEventPublisher; @Transactional public void changeValue(String value) { Example example = exampleRepository.find(1L); String beforeValue = example.getCurrentValue(); example.updateCurren..
2024.07.03
-
도메인 엔티티와 영속성 엔티티
개요소프트웨어 아키텍처에서 도메인 엔티티와 영속성 엔티티를 분리하는 것은 중요한 설계 원칙 중 하나이다. 이 원칙은 클린 아키텍처의 핵심 개념을 기반으로 하며, 도메인 로직과 영속성 계층 간의 의존성을 최소화하여 시스템의 유지 보수성과 확장성을 높이는 데 목적이 있다.이번 게시글에서는 이러한 도메인 엔티티와 영속성 엔티티의 차이점에 대해 알아보고, 이 둘을 분리함으로써 얻을 수 있는 이점과 주의해야 할 점 그리고 언제 분리하면 좋을지에 대해 정리했다. 도메인 엔티티먼저 도메인 엔티티의 개념부터 살펴보면 도메인 엔티티는 비즈니스 도메인 내의 개념을 나타내는 객체로, 전자상거래 시스템에서는 'Order', 'Customer', 'Product'등이 도메인 엔티티가 될 수 있다. 다음은 간단한 예시 코드이다..
2024.06.18
-
협업을 위해 Swagger 좀 더 잘 사용해보기
목차API 그룹화API 버전 관리 시 @Deprecated 활용하기명세만 먼저 전달하기Authorize에 jwt 넣을 때 prefix에 Bearer 생략시키기브라우저 새로고침 후에도 인증정보 유지시키기기타 자잘한 상세 설정들 1. API 그룹화Swagger에서 API를 그룹화하면 여러 엔드포인트를 논리적인 그룹으로 묶어 관리하고 문서를 체계적으로 정리할 수 있습니다.설정 후에는 위 이미지처럼 관련된 엔드포인트만 볼 수 있어 원하는 API를 쉽게 찾고 관리할 수 있습니다. Swagger에서 API를 그룹화하는 방법은 어떤 라이브러리를 사용하고 있느냐에 따라 다르기 때문에 확인해서 적용하면 됩니다.springfox-swagger2를 사용하는 경우@Configuration@EnableSwagger2publi..
2024.05.15
-
AWS Solution Architect Associate(SAA-C03) 취득 후기
왜 취득했는가?약 1년 반 정도의 경력이 쌓였지만 AWS 솔루션을 직접 다뤄본 경험은 사이드 프로젝트를 하면서 다뤄본 EC2, Lamda, S3, RDS와 같은 기초적인 서비스와 실무에서 사용하는 MSK 등 몇몇 솔루션에 대해서만 국한되어 있었다. 현업에서 기회가 없었냐 하면은 사내에서는 전사 대부분의 제품이 AWS 클라우드 환경에서 운영되고 있지만, 인프라는 DevOps팀에서 전적으로 담당해서 맡아주다 보니 실질적으로 나 같은 백엔드 개발자가 인프라를 직접 다루는 경우가 단순 연동을 위한 코드 작성 외에는 거의 없었다. 하지만 그렇다고 AWS 관련 지식이 없어도 된다는 말은 아닌데, 효율적인 소통을 위해서는 어느 정도 지식이 필요하다. 예를 들어 문제가 발생했을 때 "어제까지 잘 작동하던 것이 오늘 갑..
2024.05.06
-
AWS VPC를 사용한다면 알아야 할 네트워크 기초 지식
개요AWS를 사용하거나 다른 인프라 구조를 많이 살펴보다 보면 VPC(Virtual Private Cloud) 구조를 자주 접하게 됩니다. 이러한 VPC 구조를 이해하기 위해서는 네트워크 기초 지식이 꽤 많이 필요한데요, 이번 게시글에서는 AWS를 사용할 때 VPC의 개념과 관련 용어들에 대해 자세히 한번 살펴보도록 하겠습니다. 이번 게시글을 완독하고 나면 아래 이미지와 같은 구조를 이해할 수 있습니다. Amazon VPC(Virtual Private Cloud)란 무엇일까?Amazon VPC는 사용자가 정의하는 AWS 계정 전용의 가상 네트워크입니다. 사용자는 VPC 내에서 IP 주소 범위 선택, 서브넷 생성, 라우팅 테이블 및 네트워크 게이트웨이 구성 등 가상 네트워크 환경을 직접 구성할..
2024.04.24
-
select .. for update 대상 유무에 따른 잠금 상태
개요 InnoDB 엔진은 기본적으로 DDL 쿼리를 제외한 모든 데이터 조작 작업에서 테이블 락을 사용하지 않고 레코드 기반의 잠금 방식을 사용합니다. 더 자세히는 레코드 자체보다는 인덱스에 잠금을 설정하여 이루어지며, 테이블에 명시적인 인덱스가 없는 경우에도 내부적으로 생성된 클러스터 인덱스를 통해 잠금이 이루어집니다. 그리고 Repetable-Read 격리 수준에서 InnoDB는 record lock과 gap lock을 결합한 Next-key lock을 활용하여 Phantom Read를 방지합니다. 조건에 부합하는 특정 행을 찾기 위해 인덱스를 스캔하는 과정에서, InnoDB는 해당 인덱스 레코드뿐만 아니라 그 이전 공간에도 잠금을 설정합니다. 따라서 첫 번째 발견된 레코드와 쿼리가 정의한 범위 내의 ..
2024.04.11
-
Datadog 서버 모니터링 살펴보기
이미지를 클릭하면 큰 화면에서 더욱 자세하게 확인할 수 있습니다. Logs 먼저 가장 기본적인 로그 확인입니다. 좌측 메뉴에서 Logs를 선택하면 Datadog과 연동된 모든 서비스의 로그를 한눈에 볼 수 있습니다. 여기서 원하는 특정 서비스를 선택하여 그 서비스의 로그만을 상세하게 조회할 수도 있습니다. 로그 항목을 클릭하면 해당 로그에 대한 더 자세한 정보를 확인할 수 있습니다. 이때 Trace와 Metrics 기능이 매우 유용한데, 로그가 생성되기까지의 경로와 로그 발생 시점의 CPU 사용량, 메모리 사용량, 디스크 I/O 등의 중요한 메트릭을 상세하게 파악할 수 있습니다(Trace와 Metrics 화면은 아래에서 나올 화면과 동일하기에 아래에서 살펴보겠습니다). APM APM(Application..
2024.04.01
-
아키텍처 관점에서 늘어나는 트래픽 대응하기
개요아키텍처의 관점에서 늘어나는 트래픽을 어떻게 대응하면 좋을지에 대해 흐름에 따라 작성했습니다. 피드백은 언제든 환영이며 댓글 남겨주시면 감사하겠습니다. 한대의 인스턴스먼저 가장 간단한 구성입니다. 모든 트래픽이 한 대의 서버를 통해 관리되고 있습니다. 그리고 EC2의 IP가 유동적으로 변경되는 것을 방지하기 위해 Elastic IP를 연결시켜 놓았습니다.그러나 시간이 지나면서 사용자 수가 증가하고, 이에 따라 서버로의 트래픽도 늘어나기 시작했습니다. 이로 인해 EC2 인스턴스의 CPU 사용률이 증가하여 단일 EC2 인스턴스로는 증가하는 트래픽을 처리하는데 한계가 생기기 시작했습니다. 이 시점에서 고려할 수 있는 가장 기본적인 해결 방안은 EC2의 인스턴스 사양을 업그레이드하는 Scale-..
2024.03.24
-
내가 쓰는 IntelliJ 유용한 기능들
1. Global Data Sources 인텔리제이를 사용할 때, 각 프로젝트마다 데이터베이스(DB) 연결을 설정해 인텔리제이 내에서 직접 관리할 수 있습니다. 만약 모든 프로젝트에 걸쳐 공통적으로 사용하고 싶은 DB 연결이 있다면, 해당 연결을 Make Global로 설정함으로써 다른 프로젝트를 열어도 해당 DB 연결 설정을 유지할 수 있습니다. 2. Shelve 작업 중인 브렌치에서 다른 브랜치로 전환할 필요가 있을 때, 일반적으로는 git stash를 사용해 변경 사항을 임시로 저장한 후 이동합니다. 하지만 인텔리제이를 사용한다면 이와 유사한 기능인 Shelve를 통해 커밋하지 않은 코드를 임시로 저장할 수 있습니다. 이 기능을 통해 현재의 변경 사항을 인텔리제이 내부에 보관하고, 필요할 때 언제든..
2024.03.20
-
ThreadPoolExecutor 동작에 관한 오해
개요 Java에서 스레드풀을 효과적으로 관리할 수 있게 해주는 ThreadPoolExecutor, 그리고 이를 더욱 쉽게 사용할 수 있도록 Spring에서 제공하는 ThreadPoolTaskExecutor의 동작 방식에 관해 제가 그동안 잘못 이해하고 있었던 두 가지의 오해를 정리하고자 글을 작성하게 되었습니다. 내용은 다음과 같습니다. 별도의 설정을 하지 않으면 서버 실행 시에 지정한 스레드 풀 크기만큼 스레드가 자동으로 생성되지 않는다. corePoolSize와 maxPoolSize는 서로 상관없다. 큐의 크기를 지정해주지 않으면 maxPoolSize은 사실상 의미 없는 설정이다. corePoolSize크기 이상의 요청이 들어온다고 해서 maxPoolSize만큼 스레드를 생성하지 않는다. https:..
2024.03.11
-
[Kafka] 컨슈머 오프셋 수동으로 커밋하기
개요메시지 손실을 방지하기 위해 메시지 처리가 문제없이 완료되었을 경우에만 commit을 수행하도록, 컨슈머의 offset commit을 수동으로 설정하는 경우가 여러 존재합니다. 하지만 단순히 auto.offset.commit만 false로 지정하는 경우에는 원하는 방향으로 동작하지 않을 수도 있습니다.이번 게시글에서는 자동 커밋의 동작 과정과 주의점, 그리고 커밋을 수동으로 제어하기 위한 auto.offset.commit 설정과 ack-mode 설정에 대해 알아보겠습니다. auto.offset.commitKafka의 컨슈머는 읽은 메시지의 위치를 추적하기 위해 offset을 commit 하는 역할을 담당합니다. 이 offset은 Kafka의 내부 토픽인 __consumer_offsets에 저장되며,..
2024.03.06
-
외부 API를 연동할 때 고려하면 좋은 점들
개요최근 프로젝트에서 외부 API를 연동하는 일이 굉장히 많았었는데, 작업하면서 얻은 다양한 경험과 지식을 공유하고자 작성하게 되었습니다. 피드백은 언제든 환영이며 댓글 남겨주시면 감사하겠습니다. 1. I/O와 트랜잭션을 분리해라트랜잭션은 데이터베이스와 통신하기 위해 커넥션을 필요로 한다. 하지만 이 커넥션을 생성하는 데는 꽤 많은 비용이 들며, 대부분 이를 절약하기 위해 웹 애플리케이션 서버(WAS)는 실행 시 미리 일정 수의 커넥션 객체를 만들어 풀에 저장한다. 그리고 클라이언트의 요청이 발생하면 이 풀에서 이미 생성되어 있는 커넥션 객체를 사용하는 방식을 이용한다.그러나 커넥션 풀의 크기는 제한되어 있기 때문에, 동시에 수많은 요청들이 커넥션 풀의 개수 이상으로 오게 된다면, 커넥션을 획득하지 못..
2024.02.24
-
[Kafka] 프로듀서 멱등성 보장하기
개요멱등성은 동일한 작업을 여러 번 수행하더라도 동일한 결과가 나타나는 특성을 의미합니다. 따라서 멱등성을 지닌 프로듀서는 같은 데이터를 여러 번 전송하더라도 해당 데이터가 카프카 클러스터에 단 한 번만 저장되도록 보장합니다.그렇다면 프로듀서에 어떻게 멱등성을 적용하고, 이를 통해 데이터의 중복 저장 없이 정확히 한 번만 저장되도록 보장할 수 있을까요? 이번 글에서는 프로듀서에 멱등성을 보장하도록 적용하는 방법에 대해 상세하게 알아보도록 하겠습니다. 메시지 전송 방식Producer와 Broker의 설정 및 구성에 따라 Kafka에서는 메시지 전달 보장 수준을 아래와 같이 세 가지 방식으로 구분합니다. 최대 한 번 (At most once): 메시지가 한 번만 전송되며, 재전송은 발생하지 않는다. 그러나..
2024.02.01
-
MySQL 버전에 따른 @Transactional(readOnly=true)의 동작 과정
개요 이 글은 태현님의 블로그 게시글에서 영감을 받아 작성하게 되었습니다. 우리는 스프링 프레임워크를 통해 RDBMS를 활용하는 과정에서, 우리는 성능 최적화, 가독성 향상, 데이터 일관성 유지 등 여러 이유로 비즈니스 로직에 @Transactional(readOnly=true) 어노테이션을 자주 사용하곤 합니다. 그런데 이 어노테이션은 어떠한 원리로 동작하는 것일까요? 이번 글에서는 @Transactional(readOnly=true)의 JDBC 단계에서의 동작 과정을 위주로 살펴보려 합니다. 동작 과정 비즈니스 로직에 @Transactional(readOnly=true)을 걸고 실행하는 경우 동작하는 전체적인 과정은 아래와 같습니다. 트랜잭션 시작 데이터베이스 연결 준비 읽기 전용 상태 전파 비즈니스..
2024.01.24
-
[랭킹 알고리즘] - Hacker News Algorithm
개요공개된 Ranking Algorithm에는 가장 인기 있는 게시글을 선별하거나, 추천수를 바탕으로 순위를 매기는 알고리즘, 평점에 따라 순위를 결정하는 알고리즘 등 다양한 방식의 랭킹 알고리즘이 존재합니다. 이 중에서도 Hacker News Ranking Algorithm, Reddit Ranking Algorithm, 그리고 Highly Rated가 대표적인데, 이번 글에서는 이들 중에서도 조회수를 바탕으로 간편하게 적용할 수 있는 Hacker News Algorithm에 대해 자세히 알아보도록 하겠습니다. Hacker News Algorithm이란?Hacker News Algorithm은 Y Combinator가 운영하는 뉴스 공유 사이트인 Hacker News에서 사용하는 랭킹 알고리즘입니다...
2024.01.16
-
2023년 회고
드디어 올 것이 왔다. 2022년 회고를 쓴 게 엊그제 같은데 벌써 2023년 회고... 라니 시간 진짜 빨리 간다. 작년 이맘때쯤 취업했으니 1년 차? 아니 2년 차라 해야 하나? 여하튼 새해를 맞았으니 작년 한 해를 한번 회고해보자 한다. 작년 회고글도 그렇듯 올해도 회고글은 남들에게 보여주기보다는 내 스스로 정리하면서 한해를 뒤돌아보는 경향이 강하기 때문에 글에 두서가 없을 수도 있다.. 혹시나 지금 이 글을 읽으면서 불편함을 느꼈다 해도.. 감안해서 보면 좋겠다. 책 먼저 한 해 동안 읽은 책들은 다음과 같다. 한 달에 한 권씩은 읽는 걸 목표로 했는데 어느 정도 달성한 것 같아서 기분이 좋다. 클린코드 면접을 위한 CS 전공지식 노트 구글 엔지니어는 이렇게 일한다. 테스트 주도 개발 이펙티브 자..
2024.01.14
-
[Kafka] 리밸런싱 종류와 파티션 할당 전략
개요카프카 컨슈머는 토픽의 각 파티션에서 메시지를 처리하는 역할을 합니다. 그런데 특정 컨슈머가 문제를 겪게 되면 그 컨슈머가 처리하던 파티션의 소유권을 다른 컨슈머로 넘겨야 합니다. 이런 과정을 '리밸런싱'이라고 부르며 주로 아래 네 가지 상황에서 발생합니다.컨슈머 그룹에 새로운 컨슈머가 추가될 때기존 컨슈머가 그룹에서 나갈 때구독하는 토픽에 새로운 파티션이 생길 때컨슈머가 구독하는 토픽이 변경될 때리밸런싱이 가장 많이 일어나는 일반적인 상황은 애플리케이션 배포 시입니다. 기존 애플리케이션 종료 후 새로운 애플리케이션이 실행되면서, 기존 컨슈머가 삭제되고 새로운 컨슈머가 생성되기 때문입니다. 이 과정에서 리밸런싱이 최소 두 번 이상 발생하게 됩니다.그리고 리밸런싱 과정은 아래와 같은 문제점을 동반합니다..
2024.01.07
-
기업 정보를 수집하는 방법
공공 API 조사먼저 다른 채용 플랫폼들은 어떤 방법으로 기업 정보를 수집하고 있는지 조사를 시작했습니다. 대부분의 플랫폼들이 데이터의 출처를 명시해 놓아 조사하기는 수월했는데, 약 6개의 플랫폼을 살펴본 결과 대표적으로 나이스(NICE), 금융감독원(FSS), 금융위원회(FSC), 그리고 국민연금공단(NPS) 등에서 데이터를 제공받고 있는 것을 확인할 수 있었습니다. 그리고 더 나아가 중소벤처 24, 알리오, 클린아이 등에서도 데이터를 얻을 수 있는 것을 확인했습니다.각 데이터 출처의 웹사이트는 다음과 같습니다.나이스(NICE): https://www.niceinfo.co.kr/main.nice금융감독원(FSS): https://opendart.fss.or.kr/guide/main.do?apiGrpCd..
2023.12.28
-
Hibernate의 @Formula를 이용한 연관 관계 엔티티 집계
개요 Hibernate의 @Formula 어노테이션은 엔티티 클래스 내에서 실제로 데이터베이스 스키마에 존재하지 않는 '가상 컬럼'을 정의할 수 있는 기능입니다. @Formula를 사용하면 다른 컬럼들의 값에 기반하여 계산된 값을 표현할 수 있으며, 이 값은 엔티티를 조회할 때만 계산되어 사용됩니다. 예시 코드 @Formula 어노테이션의 전형적인 사용 사례는 연관된 데이터의 집계를 수행하는 경우입니다. 예를 들어, 게시글과 연결된 댓글의 수를 계산하는 경우를 들 수 있습니다. 아래는 간단한 예시 코드입니다. 전체 코드는 깃허브에서 확인 가능합니다. @Entity public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) pri..
2023.12.20
-
[Java] CompletableFuture로 비동기 및 병렬 처리하기
Future vs CompletableFuture Future와 CompletableFuture는 모두 Java에서 비동기 프로그래밍을 지원하기 위한 도구입니다. Java 5에서 도입된 Future는 비동기 연산의 결과를 표현하는 데 사용되며, Java 8에서 도입된 CompletableFuture는 Future를 확장하여 더 많은 기능을 제공합니다. CompletableFuture와 Future차이는 무엇일까요? Future에 대한 상세한 내용은 이번 포스팅의 주제에서 벗어날 것 같아 간단하게 CompletableFuture와의 차이점만 비교해 보았습니다. Blocking vs Non-Blocking Future는 get() 메서드를 통해 비동기 연산의 결과를 얻습니다. 하지만 이 메서드는 연산이 완료..
2023.12.12
-
Bucket4j로 트래픽 제한하기(Redis & MariaDB)
개요 최근 업무 프로젝트에서 특정(요금이 부가되는) 로직에 대해 월별 사용량을 제한하는 기능이 추가되어야 했습니다. 이와 관련하여 처리율 제한 기술을 알아보았는데 Bucket4j, Guava, RateLimitj, Resilience4j 등 다양한 라이브러리가 있는 걸 알게 되었고, 프로젝트 환경인 Spring Boot 2.7.x, MariaDB(nosql & in-momory부재), Java 11에 알맞은 Bucket4j를 선택하게 되었습니다. 다음은 고려한 라이브러리들의 특징과 그 선택 이유입니다. Guava: 다양한 핵심 Java 라이브러리를 제공하지만, 단순히 Rate Limiting 기능만을 위해 사용하기에는 다소 무거운 느낌이다. Resilience4j: 서킷 브레이커를 제공하는 라이브러리로 ..
2023.12.03
-
ShedLock으로 다중 인스턴스 환경에서 단일 스케줄러 동작 보장하기
개요 스프링에서 제공하는 @EnableScheduling 어노테이션은 간편하게 스케줄링 작업을 설정할 수 있게 해 주지만, 다중 인스턴스 환경에서는 동일한 작업이 여러 번 실행될 수 있다는 문제가 존재합니다. 이 문제를 해결하기 위해선 다양한 방법이 있지만, ShedLock이라는 오픈소스 라이브러리를 사용하면 손쉽게 해결할 수 있습니다. ShedLock은 스프링 스케줄링과 함께 사용될 수 있으며, 여러 인스턴스가 동일한 스케줄링 작업을 동시에 실행하는 것을 방지해 줍니다. 테스트 코드는 깃허브에서 확인하실 수 있습니다. ShedLock 이해하기 ShedLock은 분산된 시스템 환경에서 동일한 스케줄링 작업이 중복으로 수행되는 것을 방지하는 라이브러리입니다. 이 라이브러리는 지정된 작업에 잠금 메커니즘을 ..
2023.11.24
-
default method로 JpaRepository 좀 더 우아하게 써보기
개요 Spring Data JPA에서 JpaRepository 인터페이스를 사용할 때 Java 8에서 도입된 default method를 활용하면 기존 코드를 좀 더 깔끔하고 효율적으로 사용할 수 있다. Optional 제거하기 Spring Data JPA의 findByXX 메서드는 기본적으로 Optional을 반환한다. 이로 인해 비즈니스 로직에서 Optional 처리를 위한 추가적인 작업이 필요하게 되는데, 이럴 때 default 메서드를 활용하면 이 문제를 우아하게 해결할 수 있다. public interface UserRepository extends JpaRepository { // Default 메소드를 사용하여 findById의 Optional을 내부적으로 처리 default User fin..
2023.11.16
-
[Docker MySQL] Proxy Layer 구축하기
개요 이전 게시글 [Docker MySQL] Orchestrator를 이용한 High Availability(HA) 구축하기에서 마스터 서버에 장애가 발생했을 때 슬레이브 서버를 마스터 서버로 승격시키는 과정을 자동화하는 Orchestrator를 활용한 고가용성(High Availability, HA) 구성에 대해 살펴보았습니다. 이를 통해 마스터 서버의 모든 데이터 변경 사항이 자동으로 슬레이브 서버에 동기화되게 되며, 이를 통해 데이터의 일관성을 유지하고 마스터 서버의 부하를 효율적으로 분산시킬 수 있었습니다. 하지만, HA 구성만으로는 애플리케이션과 DB 서버 사이의 동기화 문제를 해결할 수 없습니다. 예를 들어, 애플리케이션의 설정 정보에는 장애가 발생한 db001이 마스터 DB로 설정되어 있다면..
2023.11.12
-
@Scheduled 사용할 때 스레드 설정
개요 스프링 프레임워크에서 제공하는 @Scheduled 어노테이션은 메서드에 스케줄링 기능을 부여하는 데 사용됩니다. 기본적으로 @Scheduled 어노테이션만을 사용하면 스프링은 단일 스레드에서 스케줄링 작업들을 순차적으로 처리하는데, 이는 하나의 스케줄링 작업이 완료되어야만 다음 스케줄링 작업이 실행될 수 있다는 것을 의미합니다. 따라서 부가적인 설정 없이 여러 개의 스케줄러를 작성하면, 일부 작업이 예상치 못한 시간에 동작할 수 있습니다. 예시 상황을 한번 보겠습니다. @Configuration @EnableScheduling public class ScheduledTasks { private final Logger log = LoggerFactory.getLogger(ScheduledTasks.c..
2023.11.09
-
[Docker MySQL] Orchestrator를 이용한 High Availability(HA) 구축하기
개요 이전 게시글 [Docker MySQL] Master-Slave Replication(복제) 구축하기에서 MySQL의 Master-Slave 복제 구성 방법에 대해 살펴보았습니다. 이를 통해 마스터 서버의 모든 데이터 변경 사항이 자동으로 슬레이브 서버에 동기화되게 되며, 이를 통해 데이터의 일관성을 유지하고 마스터 서버의 부하를 효율적으로 분산시킬 수 있습니다. 하지만, 마스터 서버에 문제가 발생하여 중단되면, 슬레이브 서버가 있음에도 불구하고 운영자가 직접 조치를 취하기 전까지는 슬레이브 서버를 마스터 서버로 대체하여 사용하는 것이 불가능하다는 문제가 또한 존재했었습니다. 이번 게시글에서는 이러한 문제를 해결하기 위해 마스터 서버에 장애가 발생했을 때 슬레이브 서버를 마스터 서버로 승격시키는 과정..
2023.11.06
-
[Docker MySQL] Master-Slave Replication(복제) 구축하기
개요MySQL의 Master-Slave Replication은 데이터 일관성(Consistency) 및 가용성(Availability)을 보장하기 위해 널리 쓰이는 기술입니다. 이 글에서는 Docker라는 컨테이너 도구를 활용하여 MySQL 환경에서 Master-Slave Replication을 구현하는 방법을 설명하려고 합니다. 이 과정은 크게 두 부분으로 나누어 설명하겠습니다.Master-Slave Replication 구성하기Bridge Network을 이용한 Replication 구성 Replication 동작 원리MySQL의 복제 기능은 클라이언트의 데이터 변경사항을 마스터 서버에서 슬레이브 서버로 복사하는 방식으로 작동합니다. 이 과정은 크게 4단계로 이루어집니다.변경사항의 기록: 클라이언트가..
2023.11.01
-
ArchUnit으로 아키텍처 검사하기
개요 ArchUnit은 Java 코드의 아키텍처를 검사하기 위한 간결하고 확장 가능한 오픈소스 라이브러리입니다. ArchUnit은 Java의 기본 단위 테스트 프레임워크를 활용하여, 주어진 Java 바이트 코드를 분석하고 모든 클래스의 구조를 해석함으로써 애플리케이션의 아키텍처를 체계적으로 테스트할 수 있게 해 줍니다. 패키지 및 클래스 의존성 검사: 패키지와 클래스 간의 의존 관계를 분석하고, 격리된 구조를 유지하는지 확인 상속 관계 및 순환 참조 검사: 클래스 간의 상속 구조를 분석하고, 순환 참조가 없는지 검사 레이어 아키텍처 검사: 레이어 간의 의존성을 검사하여, 명확하고 견고한 레이어 구조를 유지하는지 확인 코딩 컨벤션 검사: 사용자가 정의한 코딩 규칙을 검사하여, 일관된 코딩 스타일을 유지하는..
2023.10.28
-
504 Gateway TimeOut시 호출된 로직은?
개요 최근 작업을 하면서 504 Gateway TimeOut이 발생했고, 이에 따라 처음에는 서버 로직이 중단되었을 것이라 생각했으나 실제로는 그렇지 않았다. 어찌 보면 504는 서버(자세히는 WAS)에서 뱉은 오류가 아니기 때문에 당연한 말이지만 무심코 5xx 에러라 요청된 서버 로직도 중단될 줄 알았다. 그래서 504 Gateway TimeOut에 대해 다시 알아볼 겸 정리하게 되었다. 게이트웨이란 무엇인가? 게이트웨이는 통신 분야에서 서로 다른 네트워크를 연결을 하는 중개자 역할을 한다. 이를 통해 다른 통신 프로토콜이나 데이터 형식을 가진 네트워크 간의 소통이 가능해진다. 아래는 Nginx를 사용한 Gateway 구성에 대한 설명이다. Nginx는 웹 서버로 널리 알려져 있지만, 리버스 프록시 또..
2023.10.26
-
LocalStack을 활용한 AWS S3 테스트 환경 구축하기
개요 이번 글에서는 이전 게시글 'TestContainer로 통합 테스트 환경 구축하기'의 연장선으로, LocalStack을 이용하여 AWS의 S3 테스트 환경 구축에 대해 알아보겠습니다. 관련 코드는 깃허브에서 확인하실 수 있습니다. LocalStack이란? LocalStack은 AWS의 다양한 서비스들을 로컬 개발 환경에서 모방하여 사용할 수 있게 해주는 오픈소스입니다. 이를 활용하면, AWS의 주요 서비스들을 실제 클라우드 환경이 아닌 로컬에서 시뮬레이션하며 개발 및 테스트를 할 수 있습니다. LocalStack은 AWS의 다양한 서비스, 특히 S3, Lambda, DynamoDB, API Gateway, Kinesis, SQS 등의 주요 서비스들을 지원하며, Docker 환경에서 실행되기 때문에 ..
2023.10.22
-
TestContainer로 통합 테스트 환경 구축하기
[개요]통합 테스트 환경을 구축할 때, 데이터베이스와의 연동은 주요한 고려사항 중 하나이며, 테스트의 안정성과 신뢰성을 높이기 위해서는 실제 운영 환경과 유사한 데이터베이스 환경에서의 테스트가 필요합니다. 그러나 이러한 환경을 갖추기 위해선 여러 방법이 있고, 각각의 방법은 그 특성과 장단점이 있습니다. 이번 게시글에서는 TestContainer를 이용한 테스트 환경 구축에 대해 알아보겠습니다.전체 코드는 깃허브에서 확인하실 수 있습니다. [DB를 테스트 환경에 통합하는 방법]Local에 실제 DB를 연결하기Local 환경에 직접 DB를 실행하는 방법은 실제 환경과 유사하게 테스트를 할 수 있는 큰 장점이 있습니다. 그러나 여러 테스트를 동시에 진행하거나 데이터를 관리하는 측면에서 멱등성(idempo..
2023.10.15
-
[MySQL] MVCC를 통한 잠금없는 읽기
[MVCC(Multi Version Concurrency Control)] MVCC는 여러 트랜잭션이 동시에 같은 데이터에 접근할 때, 데이터의 일관성과 동시성을 보장하는 방식입니다. 이 기술의 핵심은 잠금(Locking) 없이 데이터를 일관되게 읽는 것이며, InnoDB 스토리지 엔진은 이를 구현하기 위해 언두 로그(Undo log)를 활용합니다. '멀티 버전'은 동시에 여러 버전의 데이터를 관리한다는 개념을 나타냅니다. 예를 들어, 다음과 같은 'member' 테이블을 생성하고 레코드를 추가하였다고 가정해 봅시다. CREATE TABLE member ( m_id INT NOT NULL, m_name VARCHAR(20) NOT NULL, m_area VARCHAR(100) NOT NULL, PRIMA..
2023.10.07
-
[MySQL] 사용자 생성 및 권한 부여 방법
[개요] MySQL은 사용자 계정의 관리 방식이 다른 DBMS와 약간 차이가 있습니다 많은 DBMS에서는 아이디로 사용자를 식별하지만, MySQL은 사용자 아이디 외에도 접속하는 IP 주소를 함께 고려하여 더욱 정밀한 접근 제어를 제공합니다. 또한, MySQL 8.0 버전부터는 '역할(ROLE)'이라는 개념도 도입되었고, 이를 통해 미리 정의된 권한 그룹, 즉 'Role'을 사용자에게 쉽게 부여할 수 있게 되었습니다. [사용자 식별] 계정과 호스트명 대부분의 DBMS는 계정 이름만으로 사용자를 식별하지만, MySQL은 사용자의 계정과 접속 지점(클라이언트 호스트명, 도메인, IP 주소)을 동시에 고려합니다. 따라서 MySQL 계정을 언급할 때는 아래의 예처럼 아이디와 호스트 정보를 명시해야 합니다. 's..
2023.10.06
-
[JVM 매개변수] InitialRAMPercentage, MinRAMPercentage, MaxRAMPercentage
개요 Java 8부터, InitialRAMPercentage, MinRAMPercentage, 그리고 MaxRAMPercentage 세 가지 JVM 파라미터가 도입되어 사용자자가 Java 애플리케이션의 힙 크기를 더 유연하게 설정할 수 있게 되었습니다. 기본적으로 JVM은 물리적 메모리에 기반하여 메모리를 할당하지만, 컨테이너 환경이 발전하면서 JVM도 컨테이너의 메모리를 기준으로 메모리를 할당하게 개선되었습니다. InitialRAMPercentage -XX:InitialRAMPercentage 파라미터는 Java 애플리케이션의 초기 힙 크기를 설정하는 데 사용됩니다. 이 값은 서버나 컨테이너의 전체 메모리 백분율로 설정되며, double 값으로 전달됩니다. 예를 들어, 1GB 메모리를 갖는 서버에서 -..
2023.10.01
-
[GA] UTM으로 유입 경로 추적하기
개요오늘날의 비즈니스는 데이터로 의사결정을 한다는 말이 있을 만큼 데이터는 중요한 의사 결정 도구 중 하나입니다. 과거에는 TV, 라디오, 잡지 등 다양한 광고 매체를 통해 제품이나 서비스를 알렸으나 그 효과를 정확히 측정하는 것이 불가능했습니다. 그러나 현재에는 사용자의 온라인 활동, 웹사이트 방문, 제품 구매 등 모든 활동을 추적하는 것이 가능해졌습니다.구글 애널리틱스(GA)는 이러한 사용자의 온라인 활동을 분석하는 데 유용한 도구로, 특히 UTM 파라미터는 웹 사이트 트래픽의 유입 경로를 상세히 추적하는 데 매우 중요한 역할을 하고 있습니다. UTM 파라미터란?UTM 파라미터란 URL의 끝에 추가되는 코드로, 사용자가 웹 사이트를 어디서 방문하였는지에 대한 상세한 정보를 제공합니다. 다들 한 번쯤..
2023.09.22
-
MySQL에서 VARCHAR와 TEXT의 차이
개요 MySQL의 문자열데이터 타입 중 VARCHAR와 TEXT는 상당히 흔히 사용됩니다. 그동안 VARCHAR는 255byte만 지원했지만, MySQL 5.0.3 이후로 VARCHAR와 TEXT 타입 모두 최대 65,535byte 길이를 지원하게 되었습니다. VARCHAR와 TEXT는 둘 다 문자열을 저장하기 위한 데이터 타입이며, 둘의 주요 차이점과 특징은 다음과 같습니다. 저장 용량 VARCHAR의 저장 구조 VARCHAR는 최대 65,535byte까지 저장이 가능하며, '(현재 저장된 byte의 크기) + (길이를 표현하는 byte)'로 구성됩니다. 예를 들어 'apple'의 경우, 문자열 'apple'은 5byte이며, 그 길이를 표현하는 바이트는 1byte이므로 총 6byte가 필요합니다. 여..
2023.09.17
-
Redis의 데이터 타입, 명령어와 활용 사례
간단 요약 데이터 타입 명령어 설명 예제 Strings SET 값 저장 `$ SET key value` GET 값 조회 `$ GET key` Lists LPUSH 왼쪽부터 값 추가 `$ LPUSH mylist value` LPOP 왼쪽에서 값 제거 `$ LPOP mylist` LINDEX 특정 위치의 값 조회 `$ LINDEX mylist 0` Sets SADD 값 추가 `$ SADD myset value` SMEMBERS 모든 멤버 조회 `$ SMEMBERS myset` Sorted Sets(ZSets) ZADD 값 및 점수 추가 `$ ZADD myzset score member` ZRANK 특정 멤버의 순위 조회 `$ ZRANK myzset member` ZRANGE 점수 범위로 멤버 조회 `$ ZR..
2023.09.05
-
분산락으로 선착순 이벤트 구현하기
선착순 이벤트 요구사항선착순으로 n명까지만 기프티콘을 받을 수 있다.기프티콘은 1인당 한번, 즉 중복으로 받을 수는 없다.저는 분산락을 통해 동시성 이슈를 해결하고, 위 요구사항을 충족하기 위해 Redis를 활용하기로 했습니다. 그 이유는, Redis는 In-memory DB로써 속도가 빠르며, RDB에서 락을 사용하기 위해서는 별도의 커넥션 풀을 관리해야 하고, 락에 관련된 부하를 RDB에서 받는다는 점이 효율적이지 않다고 생각했기 때문입니다. 분산락을 선택한 이유Redis는 "싱글스레드로 명령을 수행하므로, 애초에 동시성이 발생할 수가 없는 거 아니야?"라고 생각할 수 있습니다.일단 질문에 대한 답은 맞습니다. Redis의 명령 자체는 원자적(atomic)입니다. 하지만 아래와 같이 여러 개의 연속..
2023.08.31
-
실무에서 Redisson을 연결하면서 겪은 문제점들
Redisson을 실무에 적용하면서 겼었던 연결 문제와 해결 방법을 한번 정리해보려고 한다. 첫 번째로, 다음과 같은 오류가 발생했다. Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.redisson.api.RedissonClient]: Factory method 'redissonClient' threw exception; nested exception is org.redisson.client.RedisConnectionException: Unable to connect to Redis server: [Redis 주소] at org.springframework.beans.factory.su..
2023.08.18
-
Java에서 assert와 exception
서론 Java에서 개발자는 버그 또는 예상치 못한 상황을 검출하고 적절히 대응하기 위한 다양한 메커니즘을 사용할 수 있는데, 대표적으로 assert와 exception을 사용할 수 있습니다. 두 가지 기능은 비슷해 보이지만, 사용 목적, 적용 시나리오, 그리고 특성에서 중요한 차이점이 있습니다. Exception exception은 프로그램의 일반적인 실행 흐름에서 예상치 못한 상황이 발생했을 때, 이를 적절히 처리하기 위한 Java의 기본 메커니즘입니다. Java의 예외는 크게 두 가지로 구분됩니다. 1. Uncheked Exception(또는 RuntimeException) int x = 0; int y = 5 / x; // ArithmeticException 발생 실행 시간에 발견되는 예외로, Ru..
2023.08.11
-
Spring Data Redis의 @Indexed 사용 시 주의점
@Indexed란? @Indexed는 Spring Data Redis 모듈의 주요 어노테이션 중 하나입니다. 주 목적으로는 Redis의 보조 인덱스(Secondary Index) 생성에 사용되며, @Id가 붙여진 객체 외에도 @Indexed가 붙여진 객체로도 값을 조회할 수 있게 합니다. 예를 들어, 아래의 코드에서는 id뿐만 아니라 name으로도 데이터를 검색할 수 있습니다. @RedisHash("Person") public class Person { @Id private String id; @Indexed private String name; private int age; } 예시 한번 Person("dkswnkk", "JuHyeong", 26)을 저장한다고 가정해 보겠습니다. @Indexed 없이 ..
2023.08.07
-
WebClient에서 에러 처리와 재시도하는 방법
서론 HTTP 요청은 네트워크 지연, 일시적인 서버 오류, 잘못된 요청 등 다양한 이유로 실패할 수 있습니다. 이런 경우 애플리케이션의 실행 흐름에 문제를 일으킬 수 있기에 에러를 올바르게 처리해야 합니다. 이번 글에서는 WebClient를 사용시 HTTP요청 과정에서 발생한 에러를 처리하는 방법과 복구하기 위해 재시도 처리하는 전략에 대해 정리해 보겠습니다. 에러 처리 onErrorReturn() onErrorReturn()은 에러가 발생했을 때 주어진 default 값을 반환하는 메서드입니다. 이는 비동기 통신에서 중요한 역할을 하는데, 특히 네트워크 오류 또는 서버의 문제로 인한 에러가 발생했을 때 통신 자체를 중단시키지 않고, 디폴트 값을 반환하여 정상적으로 계속 작동할 수 있도록 할 수 있기 때..
2023.08.03