JPARespository 사용할 예정
++
V2 respository 에 @Transactional 삭제하기


AOP는 여기 없어서 체킹을 못하고 직접 가져와야 한다!

implementation 'org.springframework.boot:spring-boot-starter-aop'
AOP 라이브러리 추가

application.properties
-dev
-prod
파일 생성
- yml도 쓰는데(가독성 때문에) 아무거나 써도 상관없음
dev는 개발환경에 대한 설정파일
prod는 서비스 할때 쓰는 설정파일
예를 들어서 application.properites에
ddl-auto : create 하면 초기화 되는데
prod에서도 create로 하면 안되니까 dev, prod 로 나눠서 설정&사용함
- prod 에는 id가 아닌 배포되는 os의 환경변수를 넣음

application.properties 에 dev를 active 하겠다고 설정해준다.
→ profies을 어떤걸 active 해줄까 ? dev !

# 1. UTF-8
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
# 2. H2
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
# 3. Hibernate
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.defer-datasource-initialization=true
# 4. Dummy
spring.sql.init.data-locations=classpath:db/data.sql
# 5. Mustache Setting
spring.mustache.servlet.expose-request-attributes=true
spring.mustache.servlet.expose-session-attributes=true
# 6. Query Format
spring.jpa.properties.hibernate.format_sql=true
# 7. OpenInView
spring.jpa.open-in-view=false
이제 -prod 를 만들자.
- prod에서는 ddl-auto에 create로 하면 안됨.
spring.jpa.show-sql=true
하면 서비스할 때 로그가 찍히면 느려지니까 이것도 prod 환경에서는 지워줌.
spring.jpa.defer-datasource-initialization=true
더미데이터를 초기화 할 일이 없으니 이것도 필요없다.

mysql 디비 설정해줄때, my 라고 입력하면 2개가 뜨는데 cj가 최신꺼.
→ cj로 넣어준다.

# 1. UTF-8
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
# 2. H2
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/blogdb
spring.datasource.username=root
spring.datasource.password=1234
# 3. Hibernate
spring.jpa.hibernate.ddl-auto=none
# 4. Dummy
# 5. Mustache Setting
spring.mustache.servlet.expose-request-attributes=true
spring.mustache.servlet.expose-session-attributes=true
# 6. Query Format
# 7. OpenInView
spring.jpa.open-in-view=false
여기까지 작성하고
db 더미데이터 넣고, Board랑 User 만들기.

서버 돌려보면 dev profile가 잘 실행되고 있음

application.properties 에서 prod로 바꾸면 prod가 실행됨
board, user, boardRequest, UserRequest 만들어줌
++
팀장이 request, response 파일도 일단 만들어주기
entity, core 도 만들어주기
core 패키지 v2에서 고대로 복사해서 넣어주기.
GlobalValidationHandler에 hello어노테이션 부분 안쓰니까 삭제하기.

UserRepository 를 interface로 만든다.
→ extends JPARepository 해주기
- 인터페이스가 인터페이스를 상속할때는 extends

→ JpaRepository를 상속하면 @Repository를 안붙여줘도 된다.
interface는 new 할 수 없다.
그래서 @Repository 붙이는게 의미가 없다.

Optional은 null을 들고 있는 선물박스.
원래 값이 없으면 NoResultException이 터지는데,
Optional 안에 값이 없으면 throw 날려서 404 터트리면 된다.
++
모든걸 다 JPA 가 제공하는데 ( → 전체조회, PK조회, 삭제, 추가 등을 제공. )
업데이트는 제공안해줌 → 더티체킹 하라고 함.
→ username으로 조회하기 같은건 우리가 직접 만들어야 함

JPA를 사용할 때는 이렇게 써주면 된다.
++
이건 문법일뿐. 이해하려고 하지마
이해하는건 Persistance context같은 개념
이해하는건 개념이나 문법은 찾아쓰자
++
복잡한 쿼리를 쓸때는 일반 Repository에 넣지 말고
UserQueryRepository를 생성해서 넣어라!

복잡한 쿼리들은 여기다 쓰자!

UserService 만들고 2개를 di한다.
→ UserRepository와 UserQueryRepository !

복사해서

v3에 넣어주기
기본세팅 완료!
++

매개변수로 들어오는 User와 조회해서 가져오는 User의 변수 명이 같으면
안되고 또 헷갈리니까
⇒ 조회된거 뒤에는 PS를 붙임( → 영속성 컨텍스트에 있는 객체를 의미)
User userPS = userRepository.findByUsername(user.getUsername());
++
깃허브 Fork
협업할 때 콜라보로 안하고 fork로 하는 방법이 있다.
fork 뜨고 수정하고 PR 요청 → 깃허브로 모르는 사람이랑 협업?하는 법 !



포크뜨면 그 프로젝트가 복사된다.

→ 내 repository에서 해당 이름으로 검색됨

Sync fork → 계속 동기화 됨
Contribute → push 하고 contribute를 누르면 내 코드를 반영할 수 있다.
( 실제로는 반영요청하는 것 )
위의 Spring-teacher는 업스트림(upstream)이고,
내거는 오리진(Origin)이다.

Sync fork 를 눌러주고, Code 복사해서 로컬에 클론한다.
git pull origin master 해주고
내가 담당한 기능 구현한 뒤에
push 해주면 된다. (master에 push)
그 후

Contribute 눌러서 Pr 요청 하면 됨
++
Optional 말고 객체로 받자!
JpaRepository의 findById는 Optional로 return 하는데 반환되는 걸 XXXOP로 이름을 지어서 받는다.
Optional<Board> boardOP = boardRepository.findById(boardId);
근데 이렇게 받지 말고
boardRepository.findById(boardId).orElseThrow(() → new Exception404(”게시글이 없습니다.”);
이렇게 람다식으로 받으면서 throw 도 같이해주면
Board 타입으로 받을 수 있다.
Optional 로 받지 말고, 람다식으로 작성 후 orElseThrow 하고
Board 타입으로 받기 ! ( 변수명은 boardPS ) → 영속성 객체니까
Board boardPS = boardRepository.findById(boardId).orElseThrow(() → new Exception404(”게시글이 없습니다.”);
Java에서
Optional
객체를 만드는 방법은 여러 가지가 있습니다. Optional
은 Java 8에서 추가된 클래스이며, 이는 값이 있을 수도 있고 없을 수도 있는 컨테이너 역할을 합니다. Optional
을 사용하면 null 값을 다룰 때 발생할 수 있는 NullPointerException을 방지할 수 있습니다.다음은 Optional
객체를 만드는 몇 가지 방법입니다.1. 빈 Optional
객체 생성빈 Optional
객체를 만들려면 Optional.empty()
를 사용합니다.java
Optional<String> emptyOptional = Optional.empty();
2. 값이 있는
Optional
객체 생성값이 있는 Optional
객체를 만들려면 Optional.of()
를 사용합니다. 이 메서드는 절대 null을 허용하지 않습니다. 만약 null을 전달하면 NullPointerException
이 발생합니다.java
Optional<String> optionalWithValue = Optional.of("Hello, World!");
3. null이 될 수 있는 값을 가진
Optional
객체 생성null이 될 수 있는 값을 감싸는 Optional
을 만들려면 Optional.ofNullable()
을 사용합니다. 이 메서드는 null 값을 허용하며, 만약 전달된 값이 null이면 빈 Optional
객체를 반환합니다.java
Optional<String> optionalWithNullableValue = Optional.ofNullable("Hello, World!");
Optional<String> optionalWithNull = Optional.ofNullable(null); // 빈 Optional 반환
4. Optional 값 사용하기Optional 객체에서 값을 가져오는 방법은 여러 가지가 있습니다. 예를 들어, 값이 존재할 때만 특정 작업을 수행하려면
ifPresent()
메서드를 사용할 수 있습니다.java
optionalWithValue.ifPresent(value -> System.out.println(value));
값이 있으면 그 값을 반환하고, 없으면 기본값을 반환하려면
orElse()
메서드를 사용할 수 있습니다.java
String value = optionalWithValue.orElse("Default Value");
이와 같은 방법으로
Optional
을 활용하면 null을 보다 안전하게 처리할 수 있습니다.++
OpenInView를 false로 하면
외부에서 요청하면 트랜젝션이 종료될때 세션이 종료된다.
아무것도 트랜젝션이 안걸려있으면 ( → 서비스에 트랜젝션이 1개도 없으면 )
DB에 조회하고 세션이 바로 끊겨버린다.
// 그림
그냥 트랜젝션과
readOnly = true
트랜젝션이 있음클래스에
readOnly = true
트랜젝션 걸면 아래 매서드에 다 걸림 ( → 서비스 레이어 전체에 )트랜젝션을 걸어두면 고립성이 지켜짐.
변경(추가, 삭제, 수정) / 조회(Readonly=true)가 동시에 들어가면
→ 조회는 커밋하기 전 데이터를 본다( → 기존의 개수 1개를 본다. )
→ 만약 조회에 트랜젝션이 안걸려있으면
커밋되기전, 커밋되기 후 데이터가 다르다…
JPA는 이걸 막아둠. → 그래야 팬텀데이터가 안생긴다.
읽을때는 readonly true를 걸고 ( → 커밋되기 전의 데이터를 본다. )
→ @transactional (readOnly = true)를 서비스클래스 맨위에 붙여줌

→ readonly true를 걸면 더티체킹을 안함 ( = 연산이 좀 줄어든다 )
(원래 더티체킹을 하면 종료될때마다 뭐가 변경되었는지 변경감지를 하는데
select 이니까 변경감지를 할 필요가 없음. 변경된게 없으니까 )
→ 변경(등록, 수정, 삭제)할때는 transacitonal을 붙임
→ 즉, 여기에 걸어두면 조회 매서드에 @Transactional(readOnly=true)가 걸리는거고
변경 매서드에는 추가로 @Transactional 을 붙여주는 건가 ?
++
“게시글 저장”을 구현하는데 로그인과 회원가입이 없다.
→ 그래서 임시 session을 생성+저장한 뒤에 ( → 강제 로그인 ) 저장 기능을 만들었다.
이 임시로 만들어준 session.setAttribute는 나중에 다없애주어야 한다. ( 나중엔 로그인 기능이 있을테니 )
→ 나중에 다 수정해야 하는데 일일이 찾기가 어렵다
→ 그래서 주석을 달아주어야 한다.
→ 해당 코드 위에 // TODO: 로그인 기능이 완료되면 삭제 예정!! 이라고 주석을 달아준다.

그리고 나중에 shift 를 2번 클릭하고 , TODO 를 검색하면 TODO 주석 달아둔 곳이 다 뜬다.
→ 나중에 TODO 주석을 검색해서 한번에 다 수정한다.
→ 이렇게 안하면 나중에 못 고친다.
→ 그 외 문제 있는건 github 이슈에 올리기!

++
conflict 나서 내가 로컬에서 수정하고 push 했는데, 그전에 다른 사람들이 push 했으면
수정했음에도 불구하고 conflict 날 수 있다.
→ 이럴땐 다시 pull 받아서 로컬에서 conflict 해결하고 다시 올려야 함
오늘 fork 해서 받은 프로젝트.
나는 나대로 내 repository에 계속 업데이트해서 push 할 예정.
++
Devtool은 왜 설정해주는가
→ devtools는 컴파일 시에 리로드 해줌.
근데 인텔리제이에서 사용할땐 내부에서 추가로 설정을 해줘야 동작함.
.class로 바뀐거 를 jar 파일(실행파일)로 패키징 되어야 함.
내 컴퓨터에 컴파일된 파일이 있다고 실행되는게 아님.
실행되면서 웹서버가 만들어져야 함.
build라는 폴더 내부에 만들어져 실행됨.
reload하면 deploy됨. 웹 서버에. jar 파일이.
Share article