블로그
전체 482023. 06. 23.
1
[강의 정리][1편] 스프링 DB 1편 데이터 접근 핵심 원리 by 김영한
JDBC자바의 데이터베이스 표준 인터페이스DB 벤더 회사의 jdbc 드라이버와 함께 어떤 DBMS와도 사용 가능Sql MapperjdbcTemplate, Mybatis : sql 전달, 응답을 객체로 편리하게 변환 가능, jdbc의 반복 코드 제거ORM코드만으로 가능하다객체지향적으로 작성할 수 있으며, 동적인 쿼리의 재활용에 있어서 편리하다.Sql mapper에 비해 DB에 종속적이지 않다.JPA같은 경우 기본적인 쿼리는 자동 생성해주기 때문에 반복적인 sql 제거가 가능하다. * 내부에서는 모두 Jdbc 기반이다. DriverManagerJdbc 연결 관리각각의 DB드라이버들에게 url 정보를 체크해서 처리할 수 있는지 확인한다. Connection Pool필요한 만큼 커넥션을 미리 확보 & 커넥션 재사용대표적인 커넥션 풀 - HikariCPDataSource 인터페이스를 통해 커넥션풀에 직접 접근 없이 커넥션 획득이 가능하다DriverManager에서 DataSource를 사용하려면 DriverManagerDataSource라는 구현 클래스를 통해야한다.DriverManager은 커넥션을 위한 정보 파라미터 계속 넘겨줘야하고, DataSource는 getConnection로 커넥션을 가져온다.설정과 사용의 분리 트랜잭션데이터 베이스 작업단위원자성(all or nothing)모든 작업이 성공했을 때 commit하고, 하나라도 실패한다면 rollback-> 데이터의 일관성 유지 가능 격리성격리수준READ UNCOMMITEDREAD COMMITTED 기본REPEATABLE READ - update x, insert oSERIALIZABLE - select for updateDB 세션이 트랜잭션을 관리자동 커밋은 각각의 쿼리가 트랜잭션 단위이기 때문에 원하는 단위로 묶을 수 없다트랜잭션의 범위를 지정한다는 것은 수동 커밋을 의미한다
데이터베이스
・
강의
2023. 06. 22.
0
개발자로서 갖춰야할 태도
오류오류가 났을 때 끝까지 추적하는 것이 필요하다.첫 번째 오류를 해결하다가 또 다른 오류가 발생할 때 두 번째 오류를 해결한 후 첫 번째 오류는 기억 저 멀리로 가는 경우가 종종 있었다.오류가 있지만 실행이 되는 경우가 해당 경우인데 바로 인지하지 못하기 때문에 큰 문제를 발생시킬 수 있다.임시적으로 오류를 처리하여 근본적인 원인을 해결하지 않는 경우도 있다.대충 넘긴다면 오류는 분명히 다시 발생하기 때문에 돌아간다고 방심하지말고 끝까지 오류를 해결하는 것이 중요하다. 재사용성모듈화공통적인 기능을 묶어서 중복 코드를 방지하자.유지보수 및 가독성은 덤으로 얻을 수 있다. 코드 일원화(상위) 한 곳에서 공통으로 처리하는 것을 의미한다.관점 지향 프로그래밍이나 MSA 서버 분산화를 통해 특정 서버에 책임을 일임하는 것도 해당한다. 예외다양한 예외 케이스 고려하여 코드를 작성한다.Java는 특히 null-safety한 편이 아니기 때문에 null check에 유의하자.Validation annotation 및 Optional 활용DB슬로우 쿼리를 지양하자가벼운 select 자주 일어나는 것을 인지하며,쿼리상에서 끝낼 수 있는 작업은 코드까지 가져오지 않도록 한다.ORDER BY A, B, C로 끝낼 수 있는 작업이 코드에서는 길어진다.연관 테이블까지 호출할 경우 Join을 잘 활용하자데이터가 대용량이라면 sub query로 범위를 좁히는 것이 나을 수 있다.데이터 일괄 조회 또는 일괄 변경을 유의하여 단건 처리를 지양하자.효율적이며, 트랜잭션은 짧게 가져가는 것이 안정성에서도 좋다.여러 쿼리 또는 외부 영향을 받는 작업 단위라면 트랜잭션을 잘 묶어주자. 기타오류 발생 시 디버깅부터 하자오류가 왜 발생하는지 모르겠어서 화면을 뜷어지게 응시하며 어디가 문제인지 찾는 경우가 종종 있었다.오류가 발생하면 얼른 디버깅을 실행하여 실제 값을 따라가며 어디가 문제인지 직접 확인하자.
회고
・
태도
2023. 03. 21.
3
[도서 정리] 도메인 주도 개발 시작하기 - 최범균
최범균님의 도메인 주도 설계 관련 도서를 읽고 내용을 정리했다.도메인 주도 DDD Start! 도메인 주도 설계 구현과 핵심 개념 익히기 (2016년)도메인 주도 개발 시작하기 (2022년)DDD Start를 보완하여 출판한 도서이기 때문에 도메인 주도 개발 시작하기를 보면 된다 새롭게 알게 된 개념애그리거트같은 생명주기를 공유하는 도메인들을 의미하며, 팩토리 메서드 등으로 루트엔티티에서 로직의 일관성을 지키며 관리하도록 한다도메인 서비스기존에도 서비스를 분리하거나 필요한 유틸들을 생성하여 사용했었지만 도메인 로직을 서비스에서 관리한다는 개념 자체는 단순하지만 생각하지 못했던 부분이었던 것 같다.도메인 모델 로직을 한없이 늘리거나 응용 계층에서 일반 로직과 섞어서 복잡하게 짜지말고 도메인 서비스를 사용하는 것이 좋을 것 같다인프라스트럭처계층 아키텍처 기준으로 repository는 영속성 계층이었지만 도메인 주도 설계에서는 repository는 도메인 계층이며 실제 repository의 기능을 구현한 구현체는 인프라스트럭처 계층으로 나뉘었다도메인 주도 설계에서는 고수준 모듈과 저수준 모듈을 구분하고 고수준은 interface 로직을 호출할 뿐 저수준 구현체와는 아무런 관계를 하지 않는다 도메인 주도 설계란, 도메인을 중심으로 설계하는 것이다도메인이란 소프트웨어로 해결하고자 하는 문제 영역이다쉽게 말해서 쇼핑몰 서비스라 하면 상품, 주문, 배송 등이 해당 서비스에서 해결하고자하는 문제 영역이라 할 수 있다 도메인과 엔티티는 다르다도메인 모델 엔티티는 테이블만이 기준이 아니라 같은 단위의 속성도 묶는다엔티티의 특징은 식별자를 갖는다는 것이다Value의 특징은 값들의 묶음이라는 것이다테이블이 있다하더라도 모두 엔티티는 아니다한 엔티티에 포함되지만 DB 정규화에서 나눠진 테이블들이 있다객체로 속성을 관리하게 되면 가독성이 높아지고 상태 관리의 일관성을 높일 수 있다 애그리거트운명공동체인 객체의 묶음이다루트 엔티티에서 기능을 제공한다다른 객체에서 변경할 수 있도록 하면 일관성이 깨지고 중복 로직이 발생한다루트 엔티티가 변경 주체이지만 실제 변경은 대상 객체에게 위임할 수 있다트랜잭션 범위는 작을수록 좋으며, 한 트랜잭션에서 두 개 이상의 애그리거트를 건들지 않도록 한다서비스에서 서로 다른 애그리거트를 호출하여 변경하도록 한다JPA의 지연로딩의 개념과 같다. 즉시로딩은 객체 간의 결합도가 높아진다N+1이 발생한다면 한 번에 호출하는 join 쿼리를 생성하여 조회하도록 한다N:M 관계에서는 중간 테이블을 두어 다루는 것이 좋다논리적인 하나의 도메인은 나열식으로 생성하기보다 하나의 애그리거트에서 팩토리 메서드로 함께 생성하는 것이 좋다 도메인 주도 설계에서의 계층표현 계층, 응용 계층, 도메인 계층, 인프라 스트럭처 계층 고수준 모듈은 응용 계층과 도메인 계층이며, 저수준 모듈은 인프라스트럭처 계층이다interface가 호출되는 곳은 고수준 모듈이어야한다표현 계층, 응용 계층에 도메인 로직은 넣지 않도록 한다응용 계층에서 중복 로직이 발생한다면 메서드로 분리할 필요가 있으며, 서비스 로직이 커진다면 서비스 클래스 수준에서 분리가 필요하다응용 계층은 트랜잭션을 관리한다응용 계층(서비스 계층)은 표현 계층에 필요한 데이터만 전달한다계층 간 필요한 정보만 교환하고 서로 의존성을 줄인다보통 표현 계층에서는 값을 검증하고, 응용 계층에서는 논리적 검증/존재 검증을 한다리포지토리는 domain 계층이며, 리포지토리를 구현한 클래스는 infrastructure 계층이다infrastructure 계층 : 영속성을 구현하거나 외부와 통신하는 기능을 제공하는 레이어도메인 계층에는 도메인 모델과 도메인 서비스가 있다저수준을 추상화하는 것을 경계해야한다!저수준을 추상화하면 도메인이 구현체에 의존하게 된다 핵심 서비스 로직에서 -> 도메인 모델 로직과 부가 서비스 로직을 호출한다주문과 할인 도메인 로직이 있을 때 주문에 할인된 금액 정보가 필요하다면 해당 로직을 주문 애그리거트에 할당하지 않아야한다도메인 서비스를 생성하여 기능을 구현하는 것이 맞다 => 주문 도메인 서비스에서 할인금액을 계산하도록 한다할인 로직이 달라지는 경우, 로직을 추가하기보다 interface로 구현체를 조립할 수 있다 Bounded Context; 모델은 컨텍스트(문맥)에서 결정된다상품 모델은 카탈로그 상품, 주문 상품, 배송 상품 등 다양한 상품이 있다각 컨텍스트에서의 상품이 각각의 모델이 된다 이벤트이벤트가 발생할 때 후처리 관련한 트리거를 발생시키거나 데이터 동기화 작업을 한다예를 들어 결제는 외부 서비스를 이용하는 경우가 많은데 외부 서비스에 문제가 생겼을 때 롤백을 할 것인지, 추후에 재시도할 것인지를 결정해야한다이러한 경우 트랜잭션이나 여러 경우를 고려하여 로직을 짜게 되면 복잡한 로직으로 이어지게 되기 때문에 이벤트를 발생시켜 처리하도록 한다spring에서 ApplicationEventPublisher와 @EventListener를 사용할 수 있다비동기 처리는 다음과 같은 방식이 있다코드 수준에서 @Async를 사용메세지 큐 사용이벤트 저장소 DB 및 별도 프로그램 구축재처리 정책이나 멱등성 등을 고려하여 설계하도록 한다 CQRSCommand and Query Responsibility Segregation명령과 조회의 책임 분리DB에 대한 Write와 Read가 분리되는 것이다데이터 변경이 있을 시 -> 메세지 이벤트 발생 -> 연관 데이터 변경
도서
・
DDD
2023. 03. 03.
1
메세지 큐(Message Queue)
메세지 큐데이터를 전달 및 처리할 때 중간에 임시 공간을 두어 데이터를 쌓아두고 처리하는 것큐에 넣어두고 비동기로 처리하기 때문에 바로 처리가 필요하지 않은 대규모 데이터 - 대용량 트래픽 처리에 적합하다Pub-Sub 패턴publisher(발행자) - subscriber(구독자)비동기 메세지 패턴발행된 메세지는 불특정한 수신자로 전달된다 카프카메세지 큐 기반의 이벤트 브로커토픽 구조로 되어 있어 분산처리에 용이하고메세지 보존 기능이 있어 고가용성 특징을 갖는다
메세지큐
2023. 03. 03.
1
ELK 스택 - 로그 모니터링
ELK 스택 플로우logback -> logstash -> elastic search -> kibanaELK 스택 설명 logback로깅 프레임워크appender를 지정하여 console, logstash, slack 등으로 로그 데이터 전송 logstash로그 데이터 수집데이터 처리 파이프라인 도구elasticsearch저장 및 가공모든 유형의 데이터를 위한 무료 검색 및 분석 엔진logstash에서 elasticsearch로 데이터를 보낸다 kibana시각화elasticsearch으로부터 원하는 데이터를 가져오고 보여주는 대시보드 elastic search 검색 기능 구현logstash -> elastic search 조합elastic search의 검색 엔진 특징역색인 : 키워드 기준으로 index를 관리 전문검색 : 특정 단어가 포함된 문서를 검색할 수 있다Restful API동의어 처리 가능
ELK
・
logging
2023. 02. 19.
1
카프카 kafka 정리 2
도서 정리 - 아파치 카프카 애플리케이션 프로그래밍 with 자바 (최원영)카프카 토픽토픽은 관계형DB에서 테이블과 같으며 토픽 안에는 파티션이 있다프로듀서 -> 파티션에 적재 -> 컨슈머모든 파티션에 적재되는 것이 아닌 하나의 파티션에 적재된다queue 자료구조파티션에 들어있는 데이터를 순차적으로 소비한다파티션에 있는 데이터는 삭제되지 않는다 -> 한 번 더 가져갈 수 있다가져간 offset이 커밋(기록)된다 카프카 특징높은 데이터 처리량데이터를 묶어서 송수신하여 네트워크 통신횟수를 최소화파티션에 나눠 병렬처리컨슈머도 파티션 개수만큼 늘려 처리량 높인다직렬화, 역직렬화로 데이터 타입이 상관 없다확장성브로커 늘려서 scale out영속성처리지점을 알면 재처리 가능파일시스템에 저장함으로써 장애발생해도 서버 재가동으로 복구페이지 캐시 메모리에 저장함으로써 속도 개선고가용성프로듀서로 전송받은 데이터는 여러 브로커에 동시 저장(replication)하기 때문에 서버 장애가 발생되어도 처리가 가능하다3대부터 데이터 유실, 지연 없는 완전 복제가 가능하다 카프카 프로듀서와 컨슈머프로듀서 -> 카프카 클러스터(적절한 토픽) -> 컨슈머프로듀서프로듀서 API는 카프카의 시작점send는 비동기 응답메세지 키를 보낼 수도 있고, 커스텀 파티셔너를 생성할 수도 있다파티션 지정 가능 직렬화를 통해 바이너리 데이터(동영상)도 전송 가능컨슈머컨슈머 그룹을 기준으로 오프셋을 관리함poll로 데이터를 가져온다while(true)와 같은 반복문을 통해 지속적인 데이터 처리를 한다poll하고 commitSync를 통해 수동 오프셋 커밋을 하면 데이터 유실 및 중복을 엄격하게 관리 가능commitAsync 덜 엄격한 커밋리밸런싱컨슈머가 추가 또는 제거될 때 파티션을 컨슈멍에 재할당하는 것shutdown hook -> WakeUpException -> 종료처리 카프카 스트림즈토픽 -> 스트림즈(stateless) -> 토픽스트림즈 DSL과 프로세서 API 제공스트림즈 DSL은 많은 기능을 가진 인터페이스를 제공태스크(Task)데이터 처리 최소단위소스(root) 프로세서 -> 스트림 프로세서(처리) -> 싱크 프로세서 Kstream 키 값 형태, Ktable 유니크한 키 기준으로 데이터 처리(최신 데이터)StreamsBuilder.stream()에서 스트림 정의StreamsBuilder.filter()StreamsBuilder.join()StreamsBuilder.to() - sync 프로세서로 보낸다KafkaStreams.start()스트림 빌더에서 정의한 스트림을 실행프로듀서Properties에 각 프로세서 지정Topology.addSource().addProcessor().addSink()카프카 커넥트; 특정 작업을 템플릿화한 것프로듀서소스 커넥터; 데이터 전송SourceConnector설정 정의SourceTask데이터를 토픽으로 전송컨슈머싱크 커넥터; 데이터 처리메서드 재정의하여 사용컨버터 생성하여 사용 가능단일모드 커넥트, 분산모드 커넥트(2대 이상)파티션토픽 안의 파티션 개수를 줄이는 것은 지원하지 않는다파티션 수를 줄이려면 토픽을 삭제해야한다파티션 수는 첫 번째로는 프로듀서의 송신량을 고려하고두 번째로는 컨슈머의 수신량을 고려해야한다보통 컨슈머 처리량에 따라 파티션 개수를 맞춘다파티션은 카프카 병렬처리의 핵심이다컨슈머 처리량을 늘리거나 컨슈머를 추가하여 병렬처리량을 늘려 데이터 처리의 속도를 개선한다(메세지 키를 사용하는 컨슈머의 경우 파티션의 개수가 달라질 경우 특정 메세지 키의 파티션의 순서를 보장받지 못하는 문제가 있을 수 있다) 컨슈머 랙Lag토픽의 최신 오프셋과 컨슈머 오프셋의 차이일시적으로 파티션과 컨슈머를 늘리는 방법도 있다컨슈머 랙 모니터링 툴; 카프카 버로우프로듀서 설정/옵션프로듀서 acks1은 리더 파티션에만 저장all or -1은 전부 저장멱등성 프로듀서; 한 번만 저장enable.idempotence true트랜잭션 transactional.id를 설정하면프로듀서와 컨슈머는 트랜잭션 레코드가 존재하는 데이터만 처리한다멀티스레드 컨슈머카프카 미러메이커
kafka
2022. 11. 13.
0
Jwt 토큰 인증 플로우
JWT 플로우로그인 시 access_token, refresh token을 발급한다.발급받은 access_token을 Header에 담아서 인증 처리를 한다.Authorization: Bearer {access_token}access_token이 만료되면 refresh_token을 활용하여 access_token과 refresh_token을 재발급받는다. JWTJson Web TokenJson 형태로 사용자 정보를 암호화하여 저장하는 토큰header, payload, signature로 구성되어있다.header는 토큰의 타입과 암호화 알고리즘 정보를 담는다.payload에는 전달하는 데이터, 사용자 정보를 담는다.signature에는 header와 payload를 조합하여 서명을 생성하며, 그 과정에서 서버에서 생성한 비밀키를 사용한다.
token
・
jwt
2022. 11. 13.
0
Java - Util 생성 및 static 메서드 활용
Util 활용일회용 로직이 아닌 이후에도 다른 곳에서 사용될 기능들은 따로 분리하여 재활용static을 활용하면 JVM 메모리의 클래스 영역에 올라가며, 인스턴스 생성 없이 클래스를 통해 바로 호출하여 사용 가능하다. final은 수정되지 않는 값으로 static final을 통해 상수를 생성하여 활용 가능예시1) @DateTimeFormatter을 사용할 수 없는 버전에서 포맷터 util을 활용public static DateTimeFormatter dateTimeFormatter(){ return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); }예시2) HashMap 자료구조에서 Comparator를 사용한 정렬에서 util 활용public class ComparatorUtil { public static Comparator> getDisplayOrderComparator(){ return Comparator.comparing( (Map map)-> { return Integer.parseInt(map.get("displayOrder").toString()); } ); } public static Comparator> getInsertDateComparator(){ return Comparator.comparing( (Map map)-> { return map.get("insertDate").toString(); } ); } } productGroupMapList.stream() .sorted(ComparatorUtil.getDisplayOrderComparator() .thenComparing(ComparatorUtil.getInsertDateComparator())).collect(Collectors.toList());
java
・
static
・
Util
2022. 09. 13.
0
Job을 처리할 때 고려해야할 설계사항
JobJob이란 주기적인 데이터 일괄 처리를 의미한다.Job을 처리하기 위한 데이터는 Request 파라미터 또는 DB를 통해 호출하며 상태 변경이나 특정 기능을 실행한다. Job에서 플래그 컬럼, 부수적인 테이블 활용특정 데이터의 주기적인 업데이트가 필요할 때 모든 범위의 데이터를 스캔하면 비효율적이다.업데이트할 데이터를 선별하기 위해서 쿼리를 통해 데이터를 선별할 수도 있지만 like 조건절 같은 경우 슬로우 쿼리를 유발한다.이러한 경우 경우에 따라 다음과 같은 방법이 있다.처리여부를 나타내는 컬럼을 추가하여 처리한 데이터를 구별한다.히스토리 테이블을 활용하여 처리 완료한 경우 테이블에서 데이터를 삭제처리하여 변경이 필요한 데이터만 남게한다.처리 위치를 확인하기 위해 페이징 처리를 위한 별도의 테이블을 활용한다.
설계
・
Job
2022. 09. 13.
1
Redis와 캐시 서버
Redis란인메모리 데이터베이스로, 키-값 형태의 데이터 구조를 가지고 있다.인메로리 데이터베이스이기 때문에 디스크 입출력 없이 속도가 빠르다.휘발성을 지니고 있다.키-값 형태의 데이터 구조를 갖고 있기 때문에 유연한 데이터 관리가 가능하다. 캐시 서버나 트래픽을 분산을 위해 사용된다. Redis 구동다음 명령어로 redis가 설치된 위치에서 redis 서버를 구동시킨다.redis-server다음 명령어로 redis 클라이언트에 접속이 가능하다.redis-cli클라이언트 명령어get {key}특정 키의 값을 조회한다.key에 *를 입력할 경우 모든 데이터를 조회한다.set {key} {value}특정 키 값을 세팅한다.del {key}특정 키의 값을 삭제한다.flushall모든 데이터를 삭제 처리한다. 운영 이슈데이터 크기가 큰 경우에 redis timeout 오류가 날 수 있다이 경우, timeout을 늘려주는 것만이 방법은 아니다api를 분리하여 데이터를 쪼개서 보내거나 ttl을 늘리는 방법도 있다timeout 관련 설정timeout -> 연결된 클라이언트의 idle time과 관련이 있다. 0으로 설정하면 연결을 끊지 않는다.tcp-keepalive -> 클라이언트가 연결 끊은 상태에서 레디스 서버에서 연결을 잡고 있을 시간. 0으로 설정하면 연결을 끊지 않아 끊긴 연결이 계속해서 쌓일 수 있다.Redis cli 설정값 확인config get { timeout | tcp-keepalive | maxclients | tcp-connection)netstat -antp | grep 6379 | wc -l를 통해서 접속한 수 확인 가능하다. -> maxclients와 비교해볼 수 있다. 스프링 캐시매니저spring에서 cache를 사용할 경우 어노테이션 이외에도 cacheManager로 직접 관리가 가능하다. 의존성 주입 후에 cacheManager.getCache("캐시명")Cache 객체에서 key로 get() 호출ValueWrapper 객체에 값이 있으면 원하는 객체 형태로 형전환 후 사용 가능하다.evict 또는 put도 가능하다.
Cache
・
Redis