Replies: 2 comments
|
열심히 하시고 계시는군여 !!!!! |
0 replies
|
고생하셨습니다! DB도 팟팅 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
다양한 데이터 접근 기술에 대해 알아볼 예정.
데이터 접근 기술 진행 방식 소개
에 대해 학습할 예정.
이 기술들은 크게 2가지로 분류 가능
SQLMapper의 주요 기능
ORM 주요 기능
위 데이터 저장 기술은 하나하나 내용이 방대해, 하나하나 설명하기 보단,
왜 필요한지, 장단점이 무엇인지 설명할 예정.
강의의 목표
처음엔 메모리로 프로젝트 만들고,
JdbcTemplate
MyBatis
JPA
스프링 데이터 JPA
Querydsl
를 점진적으로 도입해보자.
프로젝트 설정과 메모리 저장소
다양한 데이터 접근 기술을 리포지토리를 바꿔가며 하나씩 적용해볼 예정.
스프링MVC 1편에서 마지막에 완성한 프로젝트
강의 코드 다운받아 itemservice-db-start 프로젝트 열자.
열어보고 각 기능들이 작동하는지 보자.
다음시간부턴 프로젝트 구조에 대한 설명.
프로젝트 구조 설명1 - 기본
build.gradle
특별한건 없고, 데이터 저장 관련 기술은 하나도 사용하지 않음. 메모리 디비 사용하기 떄문.
분석할 때는 도메인 먼저 분석을 해야함.
아이템에 해당하는 리포지토리
인터페이스로 만들었고, 향후에 이를 다른 구현체로 바꿀 예정.
검색 조건
상품명은 일부만 포함되어도 검색 가능하도록 할 예정.
업데이트를 위한 Dto
단순히 데이터를 전달하는 용도로 사용되어 Dto 가 뒤에 붙음.
DTO(data transfer object)**
ItemSearchCond도 DTO 역할을 하지만, 이 프로젝트에서Cond는 검색 조건으로 사용한다 는 규칙을 정했다. 따라서 DTO를 붙이지 않아도 된다.ItemSearchCondDto이렇게 하면 너무 복잡해진다. 그리고Cond라는것만봐도용도를알수있다.리포지토리 구현체
ItemRepository인터페이스를 구현한 메모리 저장소이다.save,update,findById는 쉽게 이해할 수 있을 것이다. 참고로findById는Optional을 반환해야하기 때문에Optional.ofNullable을 사용했다.findAll은ItemSearchCond이라는 검색 조건을 받아서 내부에서 데이터를 검색하는 기능을 한다. 데이터베이스로 보면where구문을 사용해서 필요한 데이터를 필터링 하는 과정을 거치는 것이다.itemName이나,maxPrice가null이거나 비었으면 해당 조건을 무시한다.itemName이나,maxPrice에 값이 있을 때만 해당 조건으로 필터링 기능을 수행한다.clearStore()메모리에 저장된Item을 모두 삭제해서 초기화한다. 테스트 용도로만 사용한다.서비스 코드
인터페이스
서비스를 인터페이스로 구현하는 경우는 많지 않음. 비즈니스 로직이 있기 떄문에. 로직을 수정하지 DI 로 바꾸는 경우는 거의 없음.
예제 설명을 위해 인터페이스 도입함.
구현체 -> 리포지토리에 위임.
컨트롤러
및 각종 화면 컨트롤러 있는데, MVC 강의에서 전부 다뤄본 것이므로 따로 설명 X
DTO 의 위치
따로 패키지를 둔다? OK
Service 에서 사용하는 ItemUpdateDto 를 Service 패키지에 두는것이 맞을까? 는 좀아니다.
서비스가 리포지토리를 호출하고, 결국 Dto의 최종 의존관계는 리포지토리에 있기 때문.
만약 서비스에서 사용하고 더이상 리포지토리로 넘기지 않는 Dto -> 서비스 패키지에 두는것이 맞음.
어디에 놔야해요?
서비스 전체 흐름
컨트롤러 -> 서비스 -> 리포지토리
인데 그럼 Dto 를 마지막까지 사용? 하는곳이 어디냐? 그곳에 두면 된다.
프로젝트 구조 설명2 - 설정
컨피그
빈을 수동으로등록
ItemServiceV1,MemoryItemRepository를 스프링 빈으로 등록하고 생성자를 통해 의존관계를 주입한 다.지금 서버를 띄우면 데이터가 들어가있는데, 아래 코드 때문
자동으로 호출해서 데이터 자동으로 넣어줌.
@EventListener(ApplicationReadyEvent.class): 스프링 컨테이너가 완전히 초기화를 다 끝내고, 실행 준비가 되었을 때 발생하는 이벤트이다. 스프링이 이 시점에 해당 애노테이션이 붙은initData()메서드 를 호출해준다.@PostConstruct를 사용할 경우 AOP 같은 부분이 아직 다 처리되지 않은 시점에 호출될 수 있기 때문에, 간혹 문제가 발생할 수 있다. 예를 들어서@Transactional과 관련된 AOP가 적 용되지 않은 상태로 호출될 수 있다.@EventListener(ApplicationReadyEvent.class)는 AOP를 포함한 스프링 컨테이너가 완전 히 초기화 된 이후에 호출되기 때문에 이런 문제가 발생하지 않는다.이것도 빈으로 등록되어야 작동하는데, Application 에서 등록함
@Import(MemoryConfig.class): 앞서 설정한MemoryConfig를 설정 파일로 사용한다.scanBasePackages = "hello.itemservice.web": 여기서는 컨트롤러만 컴포넌트 스캔을 사용하고, 나머지는 직접 수동 등록한다. 그래서 컴포넌트 스캔 경로를hello.itemservice.web하위로 지정했다.@Profile("local"): 특정 프로필의 경우에만 해당 스프링 빈을 등록한다. 여기서는local이라는 이름의 프로필이 사용되는 경우에만testDataInit이라는 스프링 빈을 등록한다. 이 빈은 앞서 본 것인데, 편의상 초기 데이터를 만들어서 저장하는 빈이다.프로필이란?
application.properties 에 아래 내용이 있음
스프링은 로딩 시점에
application.properties의spring.profiles.active속성을 읽어서 프로필로 사 용한다.이 프로필은 로컬(나의 PC), 운영 환경, 테스트 실행 등등 다양한 환경에 따라서 다른 설정을 할 때 사용하는 정보이다.
예를 들어서 로컬PC에서는 로컬 PC에 설치된 데이터베이스에 접근해야 하고, 운영 환경에서는 운영 데이터베이스에 접근해야 한다면 서로 설정 정보가 달라야 한다. 심지어 환경에 따라서 다른 스프링 빈을 등록해야 할 수 도 있다. 프로필을 사용하면 이런 문제를 깔끔하게 해결할 수 있다.
위에서 설정한 application.properties 때문에 프로필이 로컬인 부분이 실행 됨.
여기서 local 을 다른 이름으로 변경하면, 테스트 데이터가 들어가지 않음.
지정하지 않으면 default 로 등록됌.
프로젝트엔 test 프로필도 있음.
main 패키지 말고 test 패키지의 application.properties
application.properties는/src/test하위의 자바 객체를 실행할 때 동작하는 스프링 설정 이다.spring.profiles.active=test로 설정하면 스프링은test라는 프로필로 동작한다. 이 경우 직전에 설 명한@Profile("local")는 프로필 정보가 맞지 않아서 동작하지 않는다. 따라서testDataInit이라는 스프링 빈도 등록되지 않고, 초기 데이터도 추가하지 않는다.initData 를 저장하지 않는 이유 : 이 데이터때문에 테스트가 실패할 수 있기 때문.
프로젝트 구조 설명3 - 테스트
테스트 코드 설명
테스트는 서로 영향을 주면 안되기 때문에 각각의 테스트가 끝나면 저장한 데이터를 제거해야함.
메모리 저장소를 완전히 삭제해 다음 테스트에 영향을 주지 않도록 함.
지금 ItemRepository 인터페이스엔 clearStorage 가 없음.
그래서 MemoryItemRepository에 한해서 clearStorage 실행.
다른 데이터베이스는 트랜잭션을 롤백해서 클리어 할 예정이기 때문에 이렇게 했음.
인터페이스를 테스트하자
여기서는
MemoryItemRepository구현체를 테스트 하는 것이 아니라ItemRepository인터페이스를 테스트하는 것을 확인할 수 있다. 인터페이스를 대상으로 테스트하면 향후 다른 구현체로 변경되었을 때 해당 구 현체가 잘 동작하는지 같은 테스트로 편리하게 검증할 수 있다.데이터베이스 테이블 생성
h2 데이터베이스에 아래 명령어로 테이블 생성
generated by default as identityidentity전략이라고 하는데, 기본 키 생성을 데이터베이스에 위임하는 방법이다. MySQL의 Auto Increment와 같은 방법이다.id는 개발자가 직접 지정하는 것이 아니라 비워두고 저장하면 된다. 그러면 데이터 베이스가 순서대로 증가하는 값을 사용해서 넣어준다.insert into item(item_name, price, quantity) values ('ItemTest', 10000, 10)
로 넣어주면 아이템이 잘 저장됨
아이디가 자동 증가하네? 다른 유니크한 값으로 하면 안되나?
참고 - 권장하는 식별자 선택 전략
데이터베이스 기본 키는 다음 3가지 조건을 모두 만족해야 한다.
null값은 허용하지 않는다.테이블의 기본 키를 선택하는 전략은 크게 2가지가 있다.
자연 키보다는 대리 키를 권장한다
현재가 아니라 미래도 변하면 안되기 때문에 자연 키보단 대리 키를 권장
비즈니스 환경은 언젠가 변한다
과거에 주민등록번호를 기본 키로 잡았는데 주민등록번호를 저장하지 않는거로 법이 바뀌어서 코드 대량 수정이 발생했음.
대리 키는 비즈니스와 무관한 임의 의 값이므로 요구사항이 변경되어도 기본 키가 변경되는 일은 드물다. 대리 키를 기본 키로 사용하되 주민등록번호나 이 메일처럼 자연 키의 후보가 되는 컬럼들은 필요에 따라 유니크 인덱스를 설정해서 사용하는 것을 권장한다.
비즈니스 요구사항은 계속해서 변하는데 테이블은 한 번 정의하면 변경하기 어렵다. 그런면에서 외부 풍파에 쉽게 흔들 리지 않는 대리 키가 일반적으로 좋은 선택이라 생각한다.
All reactions