2021-03-11-TIL
Service 계층은 꼭 필요한가
Service in JPA
Spring Data JPA로 리포지토리를 만들면 컨트롤러에서 리포지토리를 바로 불러도 동작한다. 그런데도 Service 인터페이스와 구현 클래스를 따로 두는 이유가 궁금해서 Stack Overflow의 “Spring data jpa repo, why need interface service and service implementation” 질문과 Spring Data JPA 가이드, Spring Boot 기본 애너테이션 정리 글을 찾아봤다.
What is service layer role in Spring Boot
Alam Khan의 글은 DAO(리포지토리)는 DB 연결만 맡도록 가볍게 두고, 서비스 계층은 DAO와 클라이언트 사이를 오가는 데이터에 비즈니스 로직을 적용하는 자리라고 설명한다. 서비스 계층을 두는 이유로는 관심사 분리, 클라이언트가 DB에 직접 닿지 못하게 하는 보안, 그리고 결합도 감소를 든다. 예를 들어 컨트롤러 메서드 50개가 DAO 메서드 20개를 직접 부르면 DAO가 바뀔 때 50곳을 고쳐야 하지만, 그 사이에 서비스 메서드 20개를 두면 20곳만 고치면 된다는 것이다.
아래 강의 메모의 “Service도 굳이 별도로 분리할 필요가 없다”는 말과 같이 보면, 로직이 리포지토리 호출을 그대로 넘기는 수준이라면 서비스 계층은 생략할 수 있고, 여러 리포지토리를 묶거나 트랜잭션 경계가 필요할 때 의미가 생긴다고 정리할 수 있다.
AWS
프리티어는 무료로 새로운 계정을 사용할 수 있다.
Lecture
Logger 사용 권고
- System.out.println()은 안티패턴이다.
- Logger.info 보다는 Logger.debug를 사용하는 것이 좀 더 낫다.
redirect, forward, 그냥 리턴
redirect:/userHTTP 302 요청 후 처리하고 이동, 주체는 브라우저forward:userServlet 서버가 user 컨트롤러를 호출한다. -> 안티패턴이다.return "user";controller -> model -> view
301은 캐싱이 된다. 우리가 필요한 것은 302이다. 주체는 항상 브라우저가 해준다!
MDN 문서도 301 Moved Permanently를 리소스가 Location 헤더의 URL로 영구히 옮겨졌다는 뜻으로 설명하고, 이 응답은 캐시될 수 있다. 브라우저가 301을 기억해 두면 다음 요청부터는 서버에 묻지 않고 바로 새 주소로 가므로, 폼 제출 후 목록으로 보내는 것처럼 매번 서버를 거쳐야 하는 이동에는 임시 이동인 302가 맞다.
setter가 없으면 컨트롤러에 값이 안 들어온다. getter가 없으면 view에 값을 못 전달한다. 롬복을 쓰면 알아서 getters and setters를 만들어줘버려서 돌아가는 원리를 잘 모르게 된다.
ModelAndView는 사용하지 않는 것이 낫다. 실제로 ModelAndView에 String을 추가해서 동작한다. 이는 구식적인 방법
요청이 서블릿에 도달하기까지
사람 ->(http) 인터넷 ->(http) 엔진엑스 -> 톰캣(서블릿 컨테이너) -> 서블릿
JSP, 서블릿이 너무 어려움 -> J2EE가 나옴 -> 스트러츠가 나옴 -> 스프링
서블릿은 자바로 작성? HeadFirst servlet
DTO는 단순 매핑하는 역할일 때는 안 쓰는 것도 좋다.
Service도 굳이 별도로 분리할 필요가 없다.
findById().orElseThrow 쓰는 게 좋다. 그냥 userRepository.findById().get()은 하면 안 된다.
Heroku 앱 깨우기
Kaffeine은 사실 어뷰징이다..
curl -s -I http://august-qna.herokuapp.com
curl http://august-qna.herokuapp.com -I
를 쉘 스크립트로
1
* 9-22 * * * crontab /Users/august/heroku.sh >> log.txt 2>&1
crontab.guru에서 확인
자동 잔디밭 깔기 스크립트?
Heroku 무료 dyno는 한동안 요청이 없으면 잠들기 때문에, Kaffeine 같은 외부 서비스로 주기적으로 요청을 보내 깨워 두는 방법이 쓰였다. 강의에서는 이를 어뷰징으로 보고, 대신 직접 curl로 헤더만 요청하는 스크립트를 cron에 등록하는 방법을 보여 줬다. cron 표현식의 다섯 칸은 분, 시, 일, 월, 요일 순서이고, crontab.guru에 넣어 보면 * 9-22 * * *는 9시부터 22시까지 매분 실행이라는 뜻이다. 위 줄은 명령 자리에 crontab이 들어가 있는데, 실제로 실행할 것은 스크립트이므로 sh /Users/august/heroku.sh처럼 쓰는 것이 의도에 맞다. Heroku는 이후 2022년 11월 28일부터 무료 플랜을 중단했다.
서비스와 도메인
서비스는 보통 도메인 레이어에 속한다. 여러 계층에 걸쳐있는 로직이 있다면 서비스에 그냥 넣어도 될 듯
도메인은 우리가 해결하고자 하는 문제의 범위 -> 홈쇼핑 구축 - 홈쇼핑이 도메인 -> 작게 쪼개면 하위도메인 - 회원가입 도메인, 결제 도메인, 배송 도메인, 포인트 도메인
인프라 - 도메인
students에 추가, 다음, 생성, .csv 다운로드
AWS 아키텍처와 EC2 배포
강의에서 소개한 책은 『아마존 웹 서비스 클라우드 디자인 패턴 구축 가이드』(제이펍)다.
aws의 우분투 pw를 만들면 안 된다. 이유는? 보안상 안 만드는 게 낫다. pw로 들어오는 것보다 key.pem 파일이 안전하다.
맨 앞단에 로드밸런서가 있다. aws에서는 그것을 ELB가 담당한다. data center가 두 개 이상 필요한데, 하나는 AZ-a, AZ-b 서브넷을 네 개 정도 만든다. EC2가 RDS를 서브로 갖고, s3를 미디어 서버로 쓴다. 일라스티카는 레디스를 쓴다. 세션디비에 해당한다. 여기에서 세션을 퍼주는 것을 세션 클러스터링이라고 한다. 빅데이터 분석 등 다른 서비스를 쓰고 싶으면 aws에서 갖다 쓰면 된다. 이미 만들어져 있기 때문이다. 레고처럼 빌딩블록을 가져다 쓰는 것
인스턴스 > 내 인스턴스 클릭 > public ipv4
8080포트는 원래 열면 안 된다. nginx를 사용해서 앞에서 80으로 열어주고 일반 사용자는 80을 통해서만 갈 수 있어야 한다.
EC2에서 apache2를 설치해 기본 페이지를 바꿔 보고, qna 프로젝트를 받아 빌드하고 실행한 명령 기록이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
8 sudo apt install apache2
9 ls
10 apt install apache2
11 sudo apt install apache2
12 ls
13 sudo service apache2 status
14 curl http://localhost
15 ping exmaple.com
16 cd /etc/apche2
17 cd /etc/apache2/
18 ls
19 cd /ecc/
20 ls
21 cd /ec/
22 cd /etc/
23 ls
24 cd /var/www/
25 ls
26 cd html/
27 ls
28 rm index.html
29 pwd
30 ls -l
31 sudo rm index.html
32 sudo vi index.html
33 history
38 cd
39 git
40 git clone https://github.com/spring-boot-qna
41 git clone https://github.com/honux77/spring-boot-qna
42 ls
43 cd spring-boot-qna/
44 ls
45 git switch honux77
46 ls
47 java
48 sudo apt install openjdk-8-jdk
49 sudo apt update
50 sudo apt install openjdk-8-jdk
51 java
52 javac
53 javac -version
54 ls
55 ./gradle build jar
56 ./gradlew build jar
57 ls
58 ls build/libs/
59 java -jar build/libs/qna-0.0.1-SNAPSHOT.jar
60 ls
61 ./gradlew bootrun
62 history
ctrl + z and bg
EC2 -> S3 -> Spring 띄우기
미션2
PUT 메서드를 hidden 필드에 추가하니 405에러 메서드를 처리할 수 없다는 에러 페이지가 뜬다. 그래서 application.properties에
1
spring.mvc.hiddenmethod.filter.enabled=true
이와 같은 설정을 추가해주거나, 다른 방법은 시도해보았지만 실패했다.
HTML 폼은 GET과 POST만 보낼 수 있어서, Spring은 _method라는 hidden 파라미터를 읽어 요청 메서드를 PUT이나 DELETE로 바꿔 주는 HiddenHttpMethodFilter를 제공한다. Spring Boot 2.2 릴리스 노트에 따르면 이 필터는 본문에 파라미터가 있을 때 요청 본문을 일찍 소비하는 문제 때문에 2.2부터 기본으로 꺼졌고, spring.mvc.hiddenmethod.filter.enabled=true로 다시 켤 수 있다. 405가 난 이유가 이것이다.
Entity의 디폴트 생성자 접근 제한
Question 엔티티에서 값을 전달하지 못하는 에러 발생
Question에서 생성자를 통해서 엔티티를 생성하도록 이미 생성자가 정의되어 있다. 그런데 public으로 Question 생성자를 정의했더니, 값을 넘겨주지 못하고 null로 생성된 것을 확인했다. 그래서 protected로 생성자의 접근제한을 낮추었더니 우선순위가 변경되어서 기존에 멤버필드를 초기화해주는 생성자가 정상적으로 호출되어서 값이 잘 들어갔다.
Jakarta Persistence 명세는 엔티티 클래스에 인자 없는 생성자가 있어야 하고, 그 생성자는 public이나 protected여야 한다고 정한다. Hibernate는 리플렉션으로 이 기본 생성자를 호출해 엔티티를 만들기 때문에, 기본 생성자가 없으면 No default constructor for entity 예외가 난다. 기본 생성자는 JPA를 위한 것이므로 protected로 두어 애플리케이션 코드에서 빈 객체를 만들지 못하게 하는 것이 흔한 방식이다.
ResponseStatusException
ResponseStatusException usage has few benefits:
- Firstly, exceptions of the same type can be processed separately and different status codes can be set on the response, reducing tight coupling
- Secondly, it avoids the creation of unnecessary additional exception classes
- Finally, it provides more control over exception handling, as the exceptions can be created programmatically
위 목록은 Baeldung 글에서 옮겨 온 것이다. 같은 글에 따르면 ResponseStatusException은 Spring 5에서 추가된 RuntimeException으로, 예외 클래스에 @ResponseStatus를 붙이는 방식의 대안이다. @ResponseStatus는 그 예외 타입이 항상 같은 상태 코드와 메시지를 내도록 묶어 버리지만, ResponseStatusException은 던지는 자리에서 상태 코드와 사유를 정할 수 있다.
1
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Actor Not Found");
엔티티에 메서드를 추가했더니 404
1
2
3
public boolean matchPassword(User user) {
return password.equals(user.password);
}
엔티티에 이것 하나 추가했다고 갑자기 404에러가 뜬다. 즉, 뷰를 받아오지 못한다. 무엇이 문제일까?
한 번씩 서버가 완전히 종료되지 않았거나 완전히 실행되지 않았을 때 접속하려고 하면 DB 데이터를 초기화하고 받아오지 못하는 것 같다.
개발자 언어 사전
- 안티 패턴: 코드 상에서 오류 날 가능성이 큰 문제가 있는 코드이다.
- 삽질: 보통 환경구축이나 디버깅 등 뭐가 안 되는지 잘 모를 때 그것을 알아내기 위해서 부단한 노력을 하는 것을 가리킨다.
- 인사이트: 한 분야에서 계속 꾸준히 노력하다 보면 얻어지는 노하우, 보통 특정 패턴이 반복되면 그것을 통해서 구조화된 해결책이 생겨나는데, 그것을 보고 ‘인사이트를 얻었다’라고 표현한다.
TODO: Adapt @Column
TODO: include service logic in Services
참고한 자료외부 출처 14
외부 출처
- Spring data jpa repo, why need interface service and service implementation
- Guide to Spring Data JPAStack Abuse
- Spring Boot basic annotations
- What is the use of service layer in Spring Boot applications?
- Why to use Service Layer in Spring MVC
- 301 Moved Permanently
- crontab guruCronitor
- Heroku’s Next Chapter무료 플랜 중단 공지
- 아마존 웹 서비스 클라우드 디자인 패턴 구축 가이드교보문고
- Spring Boot 2.2 Release NotesHiddenHttpMethodFilter 기본 비활성화
- No default constructor for entityYawin Tutor
- Why Default or No Argument Constructor is Important in Java Class? Answer
- Jakarta Persistence 3.12.1 The Entity Class
- Spring ResponseStatusException
댓글
아직 댓글이 없습니다