전체 글
MySQL like 문 % 위치에 따라 index의 적용 여부
들어가기전.. MySQL 데이터베이스의 index의 구조는 Default가 B-tree 구조로 되어있다. 인덱스 타입 확인 show index from movies; 인덱스 정보를 조회해보면 pk와 index를 설정한 컬럼의 index type이 B-tree 구조로 되어있는 것을 확인할 수 있다. B-tree 구조는 페이지나 블록으로 나누어 오름차순 정렬을 진행하고 각각의 페이지 첫번째 값을 루트 노드 페이지에 저장하여 루트 노드 부터 리프 노드까지 탐색하는 구조다. 값을 찾으면 탐색을 멈추는 Range Scan 의 특성을 가지고 수직적으로 먼저 탐색하고 수평적으로 탐색한다. 조회의 성능을 향상시킬 목적으로 index를 설정하는데 카디널리티가 낮은 컬럼을 사용하면 오히려 성능이 떨어지고 조회 명령이 아닌..
[mysql] homebrew 로 mysql 설치 시 오류 해결
상황 mysql -uroot root계정으로 접속을 하려 할 때 에러 발생 에러 ERROR! The server quit without updating PID file ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' 위의 에러로 인해 구글링을 시작하였다. 많은 정보들이 나왔고 이를 따라 해봤는데 계속 접속이 안되어 하루 반나절을 날려먹은 경험을 적어보려합니다. 단계 첫번 째 에러같은 경우는 권한의 문제로 발생하는 문제라 나온다. 1. PID kill $ ps -ef | grep mysql 위의 코드를 사용하여 mysql PID를 킬한다. 2. 디렉토리 소유권 변경 sudo chown -R _my..
조회 하여 얻은 객체가 null일 때 예외를 발생하는 것이 좋은 것인가?
단건 조회기능을 만들 때 항상 null 체크 부분에서 예외를 던지는 상황을 마주친다. moive를 조회했는데 해당 데이터가 없는 경우, 즉 null인 객체이면 무조건 예외를 던져야 할까라는 의문이 들고, 그냥 빈 객체만 반환해도 무방한게 아닌가? 라는 생각이든다. 아래 그림을 예시로 살펴보자. 조회 된 객체를 가지고 다른 애그리거트를 생성하는것이 아닌 단지 조회이므로 예외를 던져야 하는 상황은 바람직하지 않다고 생각되고 빈컬렉션을 반환하는 것이 좋다고 생각이 든다. 이펙티브 자바를 살펴보면 null이 아닌 빈 컬렉션이나 빈 배열로 반환하라 라고 나오는데 만약 애그리거트 루트를 생성하려하고 다른 서브 애그리거트가 null일 시에는 어떻게 될까? 해당 애그리거트는 null을 가지고 생성을 하게 되고, 만약 ..
패키지 구조 변경하기
프로젝트를 진행하며 도메인과 관련된 클래스들이 많아지며 보기 좋은 패키지 구조로 변경해야 된다고 느꼈다. 왜? 계층형 구조로 만들었나? 초반에 프로젝트를 진행하며 구조를 빠르게 파악할 수 있도록 만들었다. 하지만 시간이 흐를수록 클래스가 늘어남에 어떠한 관계가 있는지 패키지만으로 판단하기가 어려웠다.. 아래 그림이 현재 패키지 구조인 계층형 구조다. 지금부터 프로젝트를 다시한번 살펴보며 패키지 구조를 변경하여 더 좋은 패키지 구조를 만드는 것이 목표가 될 것이다. 해당 프로젝트의 도메인 패키지는 아래 그림과 같다. actor, consumer, movie, reservation, screening, seat, ticket 총 7개의 도메인이 존재하고 이에 따른 repository도 추가적으로 7개가 존재..
REST API (RESTful API)
개념 REST 아키텍처 스타일의 제약 조건을 준수 RESTful 웹 서비스와 상호 작용할 수 있도록 하는 애플리케이션 프로그래밍 인터페이스(API 또는 웹 API) API(Application Programming Interface)란? 소비자에게 필요한 콘텐츠(호출)와 생산자에게 필요한 콘텐츠(응답)를 구성합니다. 예, 날씨 서비스용 API 사용자는 우편번호를 제공하고 생산자는 두 부분(첫 번째는 최고 기온, 두 번째는 최저 기온)으로 구성된 응답으로 답하도록 지정 목적 상호 작용하여 정보를 검색하거나 기능을 수행하고자 할 때 API는 사용자가 원하는 것을 시스템에 전달할 수 있게 지원하여 시스템이 이 요청을 이해하고 이행하도록 할 수 있습니다. 장점 API는 조직이 보안, 제어, 인증을 유지 관리(누..
[JPA] JPQL에서 limit절을 사용해보자
들어가기전 상황은 이렇다. 예매한 영화의 정보 중에 제일 많이 예약을 한 영화를 카운트를 하고 내림차순으로 카운트를 기준으로 정렬 한 다음 첫 번째 행의 데이터를 가져오려는 상황이다. 즉, 예매를 가장 많이 한 영화를 가져오는 기능이다. jpql로 limit query를 사용하여 query를 작성하였다. 하지만 컴파일 에러가 발생한다. nativeQuery를 사용하면 limit 절이 제공이 되지만 jpql에서는 제공이 안된다. 구글링을 통해 jpql에서 limit 키워드를 지원하지 않는다고 한다. 대신에 JPA query method의 결과를 제한하는 기능이 있다. 아래 링크를 참조하겠습니다. https://docs.spring.io/spring-data/jpa/docs/current/reference/..
애그리거트 트랜잭션 관리
애그리거트와 트랜잭션 한 주문 애그리거트에 대해 운영자는 뱅송 상태로 변경할 때 사용자는 배송지 주소를 변경할 때를 생각해보자. 한 애그리거트를 두 사용자가 동시에 변경할 때 트랜잭션이 필요하다. 트랜잭션마다 리포지터리는 새로운 애그리거트 객체를 생성하므로 운영자 스레드와 고객 스레드는 같은 주문 애그리거트를 나타내는 다른 객체를 구하게 된다. 때문에 운영자 스레드가 주문 애그리거트 객체를 배송 상태로 변경하더라도 고객 스레드가 사용하는 주문 애그리거트 객체에는 영향을 주지 않는다. 고객 스레드 입장에서 주문 애그리거트 객체는 아직 배송 상태 전이므로 배송지 정보를 변경할 수 있다. 이 상황에서 두 스레드는 가각 트랜잭션을 커밋할 때 수정한 내용을 DB에 반영한다. 이 시점에 배송 상태로 바뀌고 배송지 ..
도메인 서비스
여러 애그리거트가 필요한 기능 한 애그리거트에 넣기 애매한 도메인 기능을 억지로 특정 애그리거트에 구현하면 안된다. 억지로 구현하면 애그리거트는 자신의 책임 범위를 넘어서는 기능을 구현하기 때문에 코드가 길어지고 외부에 대한 의존이 높아지게 되며 코드를 복잡하게 만들어 수정을 어렵게 만드는 요인이 된다. 위의 문제점을 해소하는 가장 쉬운 방법은 도메인 기능을 별도 서비스로 구현하는 것이다. 도메인 서비스 도메인 영역에 위치한 도메인 로직을 표현할 때 도메인 서비스를 사용한다. 계산로직 여러 애그리거트가 필요한 계산 로직이나, 한 애그리거트에 넣기에는 다소 복잡한 계산 로직 외부 시스템 연동이 필요한 도메인 로직 구현하기 위해 타 시스템을 사용해야 하는 도메인 로직
스프링 데이터 JPA를 이용한 조회 기능
CQRS 명령 (command) 모델과 조회 (Query) 도델을 분리하는 패턴이다. 명령 모델은 상태를 변경하는 기능을 구현할 때 사용 도메인 모델은 명령 모델로 주로 사용된다. 조회 모델은 데이터를 조회하는 기능을 구현할 때 사용 정렬, 페이징, 검색 조건 지정과 같은 기능은 주문 목록, 상품 상세와 같은 조회 기능에 사용된다. 예를 들어 회원 가입, 암호 변경, 주문 취소 처럼 상태를 변경하는 기능을 구현할 때 명령 모델을 사용 주문 목록, 주문 상세처럼 데이터를 보여주는 기능을 구현할 때는 조회 모델을 사용한다. 스펙 애그리거트가 특정 조건을 충족하는지를 검사할 때 사용 메모 애그리거트에 포함되어 있는 객체를 모두 불러와 데이터 정제 작업을 하려면 메모리에 저장되는 객체 정보들이 무수히 많아진다...
응용 서비스와 표현 영역
표현 영역과 응용 영역 응용 영역과 표현 영역이 사용자와 도메인을 연결해 주는 매개체 역할을 한다. Controller 사용자와 상호 작용하는 영역은 표현 영역의 역할 표현 영역은 사용자의 요청을 해석한다. 웹 브라우저에서 폼에 입력한 데이터를 전송하면 요청 파라미터를 포함한 HTTP 요청을 표현 영역에 전달한다. Service URL, 요청 파라미터, 쿠키, 헤더 등을 이용해서 사용자가 실행하고 싶은 기능을 판별하고 그기능을 제공하는 응용 서비스를 실행한다. 표현 영역에서 파라미터로 넘어온 데이터로 어떤 서비스를 제공을 하는지는 응용 서비스의 역할이다. 웹 개발을 진행 하면서 많이 보이는 구조다. RestController 의 코드라던지 Controller 코드라던지 이러한 영역이 표현 영역이다. 응용..
리포지터리와 모델 구현
도메인 모델과 리포지터리를 구현할 때 선호하는 기술은 JPA다. 데이터 보관소로 RDBMS를 사용할 때, 객체 기반의 도멘 모델과 관계형 데이터 모델 간의 매핑을 처리하는 기술로 ORM 만한 것이 없다. 4.1 JPA를 이용한 리포지터리 구현 모듈 위치 리포지터리의 인터페이스는 애그리거트와 같이 도메인 영역에 속한다. 리포지터리를 구현한 클래스는 인프라스트럭처 영역에 속한다. 리포지터리 구현 클래스를 인프라스트럭처 영역에 위치시켜 인프라스트럭처에 대한 의존을 낮춰야 한다. 리포지터리 기본 기능 구현 ID로 애그리거트 조회하기 애그리거트 저장하기 조회 시 해당 애그리거트가 존재 하지 않으면 Null 을 반환한다. null을 반환하고 싶지 않으면 Optional을 사용해도 좋다. 애그리거트를 수정한 결과를 ..
애그리거트
3.1 애그리거트란? 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것이다. 복잡한 도메인을 이해하고 관리하기 쉬운 단위로 만드는 방법이 애그리거트 묶은 단위로 표현하는 것이다. 상위 수준의 개념을 이용해서 전체 모델을 정리하면 전반적인 관계를 이해 하는데 도움이 된다. 반대로 상위 수준에서의 개념을 파악하려면 오랜 시간이 걸린다. 도메인의 객체 모델이 복잡해지면 개별 구성요소 위주로 모델을 이해하게 되고 전반적인 구조나 큰수준에서 도메인 간의 관계를 파악하기 어려워진다. 즉, 코드를 변경하고 확장하는 것이 어려워진다. 애그리거트의 장점 모델의 이해도가 높아진다. 일관성 있게 관리하게 한다. 복잡도가 낮아져 확장하는데 도움을 준다. 3.2 애그리거트 루트 애그리거트에 속한 모든 객체가 일관성을 가지..
아키텍처 개요
2.1 네개의 영역 스프링 MVC 프레임워크가 표현 영역을 위한 기술에 해당한다. 표현 영역은 사용자의 요청을 해석해서 응용 서비스에 전달하고 응용 서비스의 실행 결과를 사용자가 이해할수 있는 형식으로 변환하여 응답한다. 응용 서비스는 로직을 직접 수행하기보다는 도메인 모델에 로직 수행을 위임한다. 핵심 로직은 도메인 모델에서 구현 2.2 계층 구조 아키텍처 계층 구조의 특성은 상위 계층에서 하위 계층으로의 의존만 존재하고 하위 계층은 상위 계층에 의존하지 않는다. 단방향 의존 인프라스트럭처에 의존하면 테스트 어려움과 기능 확장의 어려움이라는 2가지 문제가 발생한다. 이러한 문제점은 DIP로 해결한다. DIP 저수준 모듈이 고수준 모듈에 의존하게 되는 것 2.4 도메인 영역의 주요 구성요소 요소 설명 엔..
도메인 모델 시작하기
패턴이 중요한 것이 아니고 비즈니스 문제에 맞게 코드를 구성하는 것이 중요하다. 1.1 도메인 소프트웨어로 해결하고자 하는 문제 영역 여러 하위 도메인으로 구성된다. 1.2 도메인 전문가와 개발자 간 지식 공유 각 영역에는 전문가가 있다. 이들 전문가는 해당 도메인에 대한 지식과 경험을 바탕으로 본인들이 원하는 기능 개발을 요구한다. 개발자는 이러한 요구사항을 개발과 테스트과정을 거쳐 배포를 하게 된다. 이 과정이 첫 단추와 같다. 요구사항을 올바르게 이해해야 올바른 소프트웨어가 나온다. 그렇다면 요구사항을 올바르게 이해하려면 어떻게 해야 하나? 개발자와 전문가가 직접 대화하는 것이다. 다음으로 개발자도 도메인 지식을 갖춰야 한다. 알아두면 좋은 문장 Garbage in, Garbage out 잘못된 값..
[JPA] @OneToMany 단방향 매핑 이슈
일 대 다 관계의 단방향 매핑 후 save시 insert문과 update문이 추가로 나가는 이슈에 대해 정리한 글입니다. 이슈 상황 Reservation.class @Entity public class Reservation { // 생략 @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "RESERVATIONS_ID") private List seats = new ArrayList(); // 생략 } Seat.class @Entity public class Seat { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "SEATS_ID..