Browse Month

9월 2016

개인취향 JPA 사용기 3편 – Scalable Service

안녕하세요? gemini 입니다.

제가 속한 가자고 팀은 매 2주간의 스프린트 마다 추가 기능을 배포하고 있습니다.
아직 오래전 급하게 만든 코드 등 숙제들이 많지만, 많은 부분이 변하여 순조롭게 기능 확장 중입니다.

이번에 제가 공유하려는 내용은 서비스의 기능을 지속적으로 확장시키는 경험 속에서 느꼈던 점을 공유해보겠습니다.
특히 이번 글은 더 개인 취향에 의한 주관적 생각이오니 많은 의견 주시면 좋을 것 같습니다.

먼저 간단한 상황을 예제로 들어보겠습니다.

기획팀에서 아래와 같은 기능 개발 요청이 왔습니다.
1. 유저는 장바구니에 새로운 상품을 추가할 수 있어야 한다.
2. 유저가 장바구니에 담았던 누적 수량, 금액을 저장하고 있어야 한다.

자, 요구 사항을 위해 Business Logic 을 담을 CartService 를 만들어 보겠습니다.

벌써 다 만들었습니다! Cart 추가할 때에 User 를 불러와서 수량 과 금액 을 갱신 시켜 줍니다.

물론 동작에는 이상이 없지만 CartService 에서 User 를 가져와서 제어하는 형태가 제 취향은 아닌 것 같습니다.
테스트 코드 작성하기도 조금 까다로워 보이네요.

일단 제 취향대로 변경하기 위해 User 의 대한 처리를 UserService 가 수행하도록 변경해보겠습니다.

오우, 좋은 것 같습니다. 테스트 코드도 훨씬 작성하기 쉽겠네요
CartService 는 장바구니 저장만 해주고, User 의 대한 내용은 UserService 에게 맡깁니다.

그런데.. 또 다른 요구 사항으로 인해 UserService 에서도 CartService 가 필요한 상황이 발생합니다.
요구 사항을 반영하기 위해 UserService 에서 CartService 를 사용하여 작업을 진행해봅니다.

엇! 구현을 모두 끝내고 서버를 띄워보니 에러가 발생합니다.
Screen Shot 2016-09-19 at 3.02.57 AM
다들 추측하셨겠지만 UserSerivceCartService 가 상호 참조를 하고 있습니다.
이 문제는 다양한 방법으로 해결할 수 있습니다. 일단은 CartService 쪽 의 의존성을 끊어서 해결하겠습니다.

결국은 CartServiceUserRepository 를 통하여 직접 User 를 가져오고 이를 처리하게 되었습니다.
정상 동작할 것이고, 문제 될 것은 당연히 없습니다.

그렇지만 역시 제 취향은 아니네요.
저의 취향대로 Business Object 를 만들어 핵심 로직을 처리하도록 수정해보겠습니다.

이제 CartServiceUserInfoUpdater 를 사용하도록 수정하겠습니다.

이제 CartServiceUserService 와 관계가 전혀 없어졌습니다. 그리고 CartService 에서는 User 에 관하여 어떤 기능만을 사용하는지 더욱 명확해진 것 같네요.

또한 아주 약한 결합을 가지고 있기에 테스트를 작성하기가 아주 편하고 테스트도 설명하는 바가 명확합니다.

제 취향을 강조하기 위해 빡빡한 예를 들어보았습니다. 적절한 예제였는지는 잘 모르겠습니다, 조금 억지가 있어도 양해 부탁드립니다.

꾸준한 기능 확장으로 인해 Service 들에는 수십 개의 Method 가 생길지 모릅니다.
그런 상황에서 Service 들끼리 엉켜 있다면 서로를 어떤 의도로 쓰는지, 또 다른 곳에서는 어떻게 쓰이는지 예측하기가 힘들어지기 때문에 수정하기 힘들어집니다.

심한 경우에는 AService 안에서 B, C, D, E, F, G, H, I, J 서비스들을 가지고 있을 수도 있겠지요.

결론적으로 제가 공유하고 싶었던 내용은 초기에 Service 를 만들고 기능을 계속 확장해가며 너무 커져버린 Service 로 인하여 생기는 문제, 그로 인하여 점점 더 확장해나가기 힘들어지는 상황에 대한 경험 그리고 그것을 개선하기 위한 고민과 개선해본 경험을 공유하고 싶었습니다.

한 Service 가 많은 의존성을 가지고 있고 많은 일을 하고 있다면, 그 Service 가 너무 많은 책임을 지고 있다는 것입니다.

의존성과 일이 많으면 테스트하기가 까다롭고, 테스트하기가 까다로우면 확장하는데 자유롭지 못 합니다.

끝없이 요구되는 추가 기능과 버그 수정을 감당하며 문제없이 배포하기 위해서는
모든 객체들의 역할이 명확하고 테스트하기 쉬운 구조를 만들어서 객체들이 유연한 형태라면
당연하게도 확장에 유리할 것입니다.

부족한글읽어주셔서 감사합니다. 많은 생각들을 공유해주시면 감사하겠습니다. geminikims@gmail.com

AWS EC2에 엘라스틱서치 클러스터 세팅하기

가자고 개발팀은 판매 데이터 분석에 엘라스틱서치 ElasticSearch 사용을 검토하고 있다. 현재는 테스트 목적으로 AWS 서울 리전(region)에 운영되고 있는 두개 EC2 인스턴스에 각각 엘라스틱서치 노드 1개씩 세팅하고 이들(2개 노드)을 클러스터로 묶어서 운영하고 있다.

AWS EC2에 엘라스틱 서치 클러스터 세팅하기

엘라스틱 서치(이하 ES)는 브로드캐스트 방식(같은 대역의 네트워크 주소를 가진 호스트들에게 패킷을 전송하는 방식)을 이용해서 같은 대역의 네트워크에 내에 있는 다른 ES 노드들을 찾아낸다. 브로드캐스트 기능을 이용하면 같은 클러스터 이름을 가지고 있는 ES 노드들이 저절로 클러스터로 묶여 데이터를 분산 처리할 수 있다.

그러나 EC2 인스턴스에서 브로드캐스트가 막혀있기 때문에 클러스터링을 하기 위해선 직접 모든 인스턴스들을 지정해주거나(유니캐스트 unicast 라고 부른다.) 아니면 AWS Cloud 플러그인을 이용해야한다.

이 포스팅에서는 AWS 플러그인을 이용해서 클러스터링 하는 법에 대해 다룰 것이다.

구동 환경 및 버전 정보

  • AWS EC2 인스턴스
    • 1 vCPUs, 2.5 GHz, Intel Xeon Family, 1 GiB memory (t2.micro)
    • Amazon Linux OS
  • ElasticSearch
    • Version 2.3.5

EC2 인스턴스 설정

EC2 인스턴스에 엘라스틱 서치 클러스터를 설정할 때는 다음의 사항이 반드시 지켜져야 한다.

참고: 인스턴스를 하나에 먼저 ES를 세팅해놓고 이미지를 만든 후에 인스턴스를 복제하는 방식으로 다수의 AWS 인스턴스를 쉽게 만들 수 있다.

ES 설치하기

많은 리눅스 패키지가 그렇듯이 ES도 두가지 방식으로 배포가 된다. 한가지는 yum 이나 apt-get 등의 패키지 매니지먼트 시스템을 이용한 방식이고 한가지는 엘라스틱 서치 웹페이지을 통한 압축파일 배포 방식이다.
장단이 있지만, 고급 사용자가 아니라면 패키지 매니지먼트 시스템을 이용한 설치를 권장하며, 이하에서는 ?? 인스턴스 기준으로, yum 으로 설치한 상황을 가정할 것이다. apt-get(우분투)을 사용하는 경우도 크게 다르지 않다.

yum 을 이용해서 설치하기 위해선 ES 리포지토리 등록이 필요하다. 자세한 방법은 Elasticsearch Reference » Setup » Repositories의 YUM 항목을 참조하면 된다.

설치 후에 중요한 파일들의 경로들은 다음과 같다.

참고로 패키지 매니지먼트 시스템을 이용한 설치를 권장하는 이유는 다음과 같다.

  • 패키지 관리의 용이함 : 설치, 업데이트, 삭제 등이 쉬움.
  • 운영의 용이함 : 환경 변수, service 등의 등록을 별도로 하지 않아도 됨.
  • 실행파일, 설정 파일, 로그 파일 등의 표준 경로로 설정됨.

시작 프로세스 등록 (optional)

ES를 AWS 인스턴스가 새로 부팅되었을 때마다 자동으로 시작되게 할 수 있다. 참고로 yum이나 apt-get 으로 설치를 한 경우에는 /etc/init.d/ 에 elasticsearch 라는 이름의 스크립트가 생성되므로

명령만 실행하면 바로 시작 프로세스로 등록이 완료된다.

압축파일을 다운받아서 설치하는 경우에는 /etc/init.d 경로에 elasticsearch 스크립트를 직접 만들어주어야 한다. 이에 대한 내용은 참조.

플러그인 설치하기

ES 플러그인 설치는 plugin install 명령어를 통해 이뤄진다. ES 플러그인 설치시 실행하는 bin 파일은 yum 으로 설치한 경우 /usr/share/elasticsearch/bin 에 위치하고 있다.

일례로 elasticsearch-head, elasticsearch-HQ, cloud-aws 플러그인을 설치하는 경우엔 /usr/share/elasticsearch/bin 디렉토리 내에서 다음과 같이 명령한다.

참고로 위의 플러그인들 중에 cloud-aws 는 EC2 클러스터링을 위해서 반드시 설치해야 한다.

ES 설정하기

AWS ES 클러스터링의 핵심은 설정파일(elasticsearch.yml)에 있다. 클러스터링을 하기 위해서는 설정파일에서 다음의 조건들이 지켜져야 한다.

  • 같은 클러스터로 묶이는 인스턴스들은 cluster.name 이 모두 같은 이름으로 설정되어야 한다.
  • node.name 은 각 인스턴스를 구분할 수 있도록 지어준다.
  • network.host 는 각 ES 인스턴스가 구동되고 있는 AWS EC2 인스턴스의 private IP 를 적어준다.
  • cloud.aws.region 은 ES 인스턴스의 region
  • discovery 속성은 클러스터링을 위해 같은 클러스터 이름 cluster.name 을 가진 ES 인스턴스들을 검색하기 위한 정보들을 설정해준다. 이 때 검색의 범위는 같은 EC2 region 에 속한 EC2 인스턴스로 제한된다.
    • discovery.typeec2로 입력한다.
    • 만약 현재 해당 AWS region에 구동되고 있는 EC2 인스턴스의 개수가 많다면 discovery.ec2 속성에 다음과 같이 입력해주는 것이 좋다. 이는 클러스터링을 위한 노드 검색의 범위를 같은 security group 을 가진 EC2 인스턴스로 제한한다.
    • discovery.ec2.any_group: false
    • discovery.ec2.groups는 해당 EC2 인스턴스의 security group.

즉 예시로, 가자고 ES 인스턴스의 설정파일은 다음과 같이 작성되어 있다. (private IP와 security group ID는 보안상 주석으로 대신했다.

실행 테스트

yum install 을 했다면 /etc/init.d 에 별도의 스크립트를 등록하지 않아도 service 를 이용해서 ES를 시작할 수 있다.

실행이 정상적으로 됐다면 브라우저에서 EC2 public ip 9200 포트로 접속해보자. 즉 http://<YOUR EC2 PUBLIC IP>:9200로 접속해보자.

위와 같이 뜬다면 정상이다. (<…> 에는 해당 ES 노드의 정보가 뜰 것이다.)

클러스터링이 제대로 됐는지 확인하기 위해서 http://<YOUR EC2 PUBLIC IP>:9200/_cat/nodes?v 로 접속해보자. 아래와 같이 클러스터로 묶인 노드들의 정보가 정상적으로 나오는지 확인해보자.

by EastskyKang

레저큐 인턴기 6편 – 백엔드 개발 도전! (2)

웹API 컨트롤러의 레이어드 아키텍쳐

전편에서 이어집니다…

가자고 웹API의 컨트롤러는

  • Controller 레이어: 가장 상위에서 유저의 리퀘스트(request)를 받고 리스폰스(response)를 돌려줌.
  • Service 레이어: Controller 밑에서 실질적인 비지니스 로직의 구현을 담고 있음.
  • Repository 레이어: DB와의 인터페이스를 담당.

이상의 세개 레이어(layer)로 구성되어 있다. 이렇게 하나의 시스템을 여러개의 레이어(층)로 분리하고 차곡 차곡 쌓아 올라가는 식으로 개발하는 방식을 ‘레이어드 디자인 패턴(Layered Design Pattern)’이라고 부르는데, 소프트웨어 엔지니어링에서 가장 보편적인 디자인 패턴 중 하나이다.

여러가지 이유가 있겠지만 이렇게 레이어를 나누는 가장 중요한 이유는

  1. 규모가 큰 시스템을 여러개로 모듈화하여 한 모듈의 문제가 시스템 전체로 번져나가는 것을 막음과 동시에 각 모듈을 쉽게 교체하기 위함이자, (종속성의 최소화, 재사용성)
  2. 오로지 하나의 이슈를 해결하도록 분리된 각 층을 추상화함으로써 컴포넌트 간에 인터페이스를 쉽게 하고 개발을 용이하게 하기 위함이다. (표준의 지원)

Screenshot 2016-09-06 14.59.49.png
가자고 웹API의 컨트롤러는 Controller, Service, Repository 등의 레이어로 구성된 레이어드 아키텍쳐(Layered Architecture)를 따른다.

레이어드 아키텍쳐 관점에서 개발 미션 바라보기

이쯤에서 나의 개발 미션이 무엇이었는지 다시 떠올려보자.

입력한 조건에 따라 DB에서 서비스 운영에 필요한 통계자료를 추출하는 웹 API를 새로 만들기

개발 미션을 가자고 웹API 컨트롤러의 레이어드 스트럭쳐 관점에서 바라보면 구현 단계를 가장 밑에서부터 네단계로 쌓아 올라가는 것으로 압축할 수 있다.

  1. Model 클래스 구현: 쿼리로 조회한 데이터를 담을 모델 클래스 만들기.
  2. Repository 구현: 데이터 추출 SQL 쿼리를 자바로 구현하기.
  3. Service 구현: 비지니스 로직을 코드로 구현하기. 즉, Repository에서 쿼리로 가져온 데이터들을 어떻게 가공하고 Controller에 다시 돌려줄 것인지 고민하기.
  4. Controller 구현: API에 URL 자원을 RESTFul하게 할당하고 유저의 리퀘스트 처리. 리스폰스 리턴.

사실 Repository 레이어에서 DB로부터 데이터를 가져오는 것을 빼고는 복잡할 것이 없는 작업이다.

  • Model 클래스에서는 내가 필요한 정보(DB의 필드에 해당한다)가 무엇인지 고민하고 이 값들을 담을 수 있는 변수들을 멤버로 가지고 있는 클래스들을 새로 만들어주면 된다. (클래스 만들기는 자바의 기본!)
  • Service에서는 Repository에서 돌려준 데이터들의 묶음(Collector: List, Map 등…)을 엑셀 템플릿과 엑셀 라이브러리를 이용해서 엑셀 파일로 출력해주면 된다. (엑셀 라이브러리만 가져다 쓰면 된다!)
  • Controller는 사실상 스프링 프레임워크에서 제공되는 라이브러리들을 이용하면 자바 어노테이션과 몇 줄 안되는 코드로 구현이 가능하다. (스프링이 알아서 다 해준다…)

자 그렇다면 이번 개발 미션의 핵심이 무엇인지 드러난다. Repository에 SQL SELECT 쿼리를 자바로 구현하는 것이 이번 개발 미션에서 가장 중요한 부분이다.

JOOQ로 SQL SELECT 쿼리를 자바로 구현하기

가자고 어플리케이션은 DB 인터페이스에 JPA(Java Persistence API: 자바 영속성 API)와 JOOQ(Java Object Oriented Querying)를 이용한다.

JPA는 자바 진영의 표준 ORM(Object-relational mapping) 기술로, 관계형 DB를 객체지향패러다임 언어인 자바에서 쓰기 위한 맵핑을 담당하는 기술이다. 표준 영속성(여기서 영속성이란 프로그램이 종료되어도 사라지지 않는 속성, 즉 DB 데이터의 속성을 의미한다) API인 만큼 자바를 이용해서 백엔드 개발을 하는 개발자들이 익히 잘 알고 있는 기술이다.

반면에 JOOQ는 다소 생소한 이름일 수 있다. 스위스 취리히 소재의 스타트업 Data Geekery이 운영하고, 개발하고 있는 JOOQ는, SQL 쿼리를 자바로 구현해야 하는 상황에 이용하는 라이브러리이다. 즉, 미리 작성해 놓은 SQL 쿼리를 그대로 자바로 옮기고 싶다거나, 아니면 JPA로는 구현이 힘든 매우 복잡한 SQL 쿼리를 짜야하는 경우에 멋진 대안이 된다. (참고로 이런 목적으로 사용하는 또 다른 라이브러리 중에 유명한 것이 iBatis 란 기술이다. 가자고 시스템에도 iBatis를 사용한 적이 있었지만 지금은 대부분 JOOQ로 대체하고 있다.)

좀 더 와닿는 코드 예제로 살펴보자. 백엔드 어플리케이션에서 아래와 같은 SQL 쿼리를 사용할 일이 있다고 가정하자. (코드 출처는 wikipedia.org – Java Object Oriented Querying)

위의 SQL 쿼리는 STATUS가 ‘SOLD OUT’인 BOOK 들의 AUTHOR 정보를 가져오는 테이블이다. JOOQ를 이용해서 자바로 변환하면, 이렇게 짤 수 있다.

JOOQ를 이용하면, SQL 문법과 상당히 유사한 자바 코드로 SQL 쿼리를 작성 할 수 있다. 자 그렇다면 이제 JOOQ를 이용해서 내가 작성해두었던 SQL SELECT 쿼리들을 자바로 옮길 차례다.

keep-calm-and-code-on
나우 잇츠 타임 포 코딩!

하나만 간단히 살펴보자. 가자고 서비스 운영에 필요한 정보 중에 특정 아이템을 구매한 고객들의 이름, 이메일, 전화번호를 알아야 하는 경우가 있었다. 이 리스트를 추출하기 위해서 아래와 같은 SQL SELECT 쿼리를 작성했었다.

이 것을 새로 생성한 Repository 클래스의 public List<FrontUser> getItemUserStat(List<String> itemCode) 메서드에 구현했다.

잠시 코드를 살펴보자. 먼저 해당하는 SQL SELECT 쿼리를 JOOQ를 이용해서 만들고 나서, fecthInto(FrontUser.class) 메서드를 호출했다. 이 메서드는 FrontUser 라는 모델 클래스의 리스트(List<FrontUser>)를 리턴하는 것을 확인 할 수 있다. 따라서 이 메서드에, 조회하고 싶은 아이템들의 itemCode를 List<String> 로 전달하면 해당하는 사용자의 정보를 FrontUser 모델 클래스의 리스트로 얻을 수 있다.

이렇게 JOOQ로 쿼리를 짜면 쉽고 간단하게 쿼리를 만들 수 있다. IDE의 자동완성 기능의 도움을 받는다면 일일이 JOOQ의 문서나 매뉴얼을 뒤져보지 않아도 그 자리에서 바로 SQL 쿼리를 자바로 옮길 수 있다.

이렇게 완성된 Repository 메서드를 이제는 거꾸로 Service에서 이용하고, 마지막으로 Controller에서 Service 메서드를 호출하면 SQL로 DB에 쿼리를 날렸을 때와 동일한 데이터를 자바 객체 묶음(Collector: List, Map 등등)으로 얻을 수 있었다. Controller의 각 메서드에 이제 RESTFul 한 URL을 할당해주기만 하면 웹 API가 완성된다!

새로운 세계

그렇게 작성한 나의 첫 백엔드 코드를 커밋(Git Commit)하고, 마침내 나의 풀리퀘스트(Pull Request)가 master 브랜치에 머지(merge) 되는 순간, 새로운 세계가 열렸다! 얕게는 매일 SQL 쿼리를 짜고 DB에서 데이터를 긁어오는 반복 노동으로부터의 해방된 것이고, 깊게는 백엔드 개발의 프로세스에 대한 경험을 해보게 된 것이다. 무엇보다 기뻤던 것은 드디어 가자고 프로젝트에 유의미한 기여를 할 수 있게 되었다는 사실이었다.

물론 이 것은 시작일 뿐이었다. 작동하는 웹 API를 만들어보기는 했지만, 웹 어플리케이션이라는 것이 무엇인지, 어떻게 동작하는지 제대로 이해하게 된 것도 아니고, Spring, JOOQ, JPA와 같은 기술들의 문서를 숙지하고 개발한 것도 아니고, 더군다나 효율적인 개발 프로세스대로 개발을 해본 것도 아니다. 그렇지만 내가 만든 API가 제대로 가자고 웹 어플리케이션 위에서 동작한 것을 확인했을 때의 즐거움이란 어마어마한 것이었다. 하하! 드디어 나도 이제 웹개발자구나! 🙂

여튼 이렇게 웹 개발의 ‘ㅇ’도 모르고 있었던 내가 DB를 다루고 관리하는 일부터 시작해서 백엔드 어플리케이션을 개발하는 백엔드 개발자의 업무를 경험해보기 까지 1달이 좀 못되는 시간이 걸렸다. 물론 그때는 몰랐다. 백엔드 개발에 더 익숙해지는데까지는 훨씬 더 많은 시간이 걸릴 것이라는 것을…

백엔드 개발 도전! 편 끝. 레저큐 인턴기는 계속 이어집니다…
By EastskyKang

개인취향 JPA 사용기 2편 – Entity with Getter,Setter and Test

안녕하세요? gemini 입니다.

지난 1편에서는 기본적인 Spring Boot + Gradle + JPA 설정을 해보고, 기본적인 Entity 를 생성해봤습니다.

오늘은 Entity 의 Getter, Setter 그리고 Test 에 대한 제 개인적인 견해와 활용방법을 공유하려 합니다.

우선 1편에서 다룬 User Entity 를 보겠습니다.

name, phone 필드는 Getter, Setter 를 모두 가지고 있습니다만, id 필드는 Getter 만 존재합니다.

Setter 를 만들지 않은 이유는 id 필드의 수동적 제어를 막기 위함입니다. 애초에 id 필드를 제어 불가로 만들어 혹시 모르는 실수를 막는 것입니다.

또한 기존 코드는 lombok 을 사용하지 않았습니다만.
제 취향대로 User Entity 에 iombok 을 적용해보겠습니다.

우선 저는 다른 기능을 사용하지 않고 @Getter Annotation 만 사용하였습니다,
또한 User Entity 에서 Setter 를 모두 제거하였습니다.

Setter 를 삭제한 이유는 이렇습니다. JPA 는 Transaction 안에서 Entity 의 변경사항을 감지히여 UPDATE SQL을 생성합니다.

즉 Setter 가 Update 기능을 수행하고 있습니다. 이런 상황에서 무분별하게 Setter 를 쓰게 된다면,
예측하지 못한 필드가 Update 가 될 때 Setter 를 일일이 체크해야 합니다.

그럼 Setter 없이 어떻게 데이터 수정을 할까요? 수정 된 User Entity 를 보겠습니다.

updateInfo Method 를 통해서 수정이 가능해졌습니다 어떻게 보면 Setter 가 더 유연해 보일 수도 있습니다. name 만 수정하고 싶을 수도 있기 때문이죠

모든 Entity Method 를 기능화 시키긴 힘듭니다. 그렇지만 생각 없이 @Setter 를 달고 무분별하게 Setter를 사용하는 것은 지양하는 게 좋다고 생각합니다.

이제 앞으로 추가될 모든 Entity의 Primary Key 를 id 필드로 제한하고 싶습니다.

여러 이유가 있지만 기본적으론 PK 를 변경해야 하는 상황이 왔을 때 유연하게 대처하고 싶기 때문입니다.

JPA 는 @MappedSuperclass Annotation으로 Parent Entity Class 를 지원합니다.

그럼, 모든 Entity 의 부모가 될 BaseEntity 를 작성하겠습니다.

추가적으로 모든 Entity 의 생성시간, 수정시간을 기록 할 수 있게 @PrePersist, @PreUpdate Annotation 을 사용했습니다.

이제 User Entity 가 BaseEntity 를 상속받게 구현하겠습니다.

변경된 User 입니다. 자식 Entity 쪽에서는 id 필드가 사라졌으나.

여전히 Id 는 외부 수정이 불가능하고 오직 Persist 시에만 생성됩니다.

다른 팀원들이 BaseEntity를 상속받는 규칙을 지키면 모든 Entity 는 id 필드를 Primary Key 를 가진 형태가 될 것입니다.

자 그럼 조금 더 제 취향에 맞게 변경되었으니, User Entity에 대하여 단위 테스트를 작성해봅시다.

정상적으로 기능이 동작하는 것을 볼 수 있습니다.

위의 테스트는 단순하지만 연관관계가 엮인테스트 또는 id 필드가 반드시 있도록 mocking 해야 하는데
id 필드를 어떻게 설정할 것인가? 이제 와서 Setter 를 추가할 것인가? 아니면 테스트를 하지 않을 것인가?

그럼 이제 test Module 에 아래와 같은 클래스를 만듭니다.

지저분 해 보이기도 하고 그냥 Setter 만들고 싶어질 수도 있습니다. 자 그래도 만들었으니 사용해보겠습니다.

이제 테스트 시에만 id 가 존재하는 Entity 생성이 가능합니다.

저의 경우는 테스트 코드 작성 시 Builder 를 만들어서 사용하는 것을 지향하기에 Builder 내부에서 MockEntity 을 사용할 것 같습니다.

굳이 이렇게까지 id 의 Setter 를 제한할 필요는 없을 수도 있습니다. Setter 쓰는 곳 찾는 게 어렵진 않습니다.

그럼에도 제 생각엔 객체의 행위들은 의미가 있어야 하고, 낭비적 코드를 줄여야합니다.
코드를 간결하고 유의미하게 만드는 것이 가장 중요하다고 생각합니다.

어떤 객체는 모든 필드에 대한 Setter 가 존재해야 할 수도 있습니다.
중요한 것은 무작정 @Setter Annotation 부터 선언하지 않는 것이라고 생각합니다.

두서 없는글 읽어주셔서 감사합니다. 피드백은 언제나 환영입니다. geminikims@gmail.com

**해당 프로젝트 소스는 공유 예정입니다.

– 개인취향 JPA 사용기 1편