-
Notifications
You must be signed in to change notification settings - Fork 132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Spring MVC] 한성재 미션 제출합니다. #358
base: bingle625
Are you sure you want to change the base?
Conversation
- 예약 DB schema.sql 추가
- 예약 조회 테스트
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
전체적으로 잘 해주셨습니다.
별도로 jdbctest 패키지만들어서 연습한것도 인상적이네요!!
몇몇부분들에 대해 리뷰남겼는데 확인해주시고, 궁금한거 편하게 물어봐주세요!
@@ -14,8 +14,15 @@ repositories { | |||
|
|||
dependencies { | |||
implementation 'org.springframework.boot:spring-boot-starter' | |||
implementation 'org.springframework.boot:spring-boot-starter-web' | |||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
타임리프를 추가하셨군요! 이번 미션에선 사용 안한 것 같은데, 어떤 이유로 쓰셨나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트하다가 지우지 않은 흔적으로 보입니다..;
package jdbctest; | ||
|
||
public class Customer { | ||
private long id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
자바엔 long과 Long이 있습니다. 둘은 무엇이 다르고 각각 어떤 장단점이 있나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrapper 클래스의 차이는 null값을 가질 수 있냐, 없냐 로 알고 있는데, 좀더 자세히 알아보고 정리해보겠습니다.
long 과 Long 타입의 차이
1. long은 원시(Primitive) 타입, Long 타입은 참조(Reference) 타입
-
원시 타입은 변수에 데이터 자체를 저장함.
-
참조 타입은 변수에 데이터가 저장된 주소를 저장함.
-
원시타입은 null 할당 불가능, 참조타입은 null값 할당 가능
-
원시타입은 값이 stack에 저장됨.
-
참조 타입은 주소가 stack에 저장되고, 실제 데이터는 heap에 저장되기 때문에, 원시타입이 더 성능상의 이점이 있음.
-
보통 엔티티의 Id 가 Long 타입인 이유는, null 값이 배정될 수 있는 편이 여러모로 편하기 때문
-
의문이 생긴 부분1 : 출처에서 원시타입은
실제 메모리에 값을 저장함
이라고 했는데, 참조 타입도 실제 메모리에 값을 저장하지 않나? -
의문이 생긴 부분2 : heap과 stack?
추가 조사: heap과 스택
자바 프로그램 실행시, JVM(자바가상머신)은 하드웨어로부터 전달받은 메모리를 특정 부분들로 나눈다.
메모리 공간(Runtime Data Area)
- Method(static) 영역
- Stack 영역
- Heap 영역
데이터 타입(자료형)에 따라 각 영역에 나눠서 할당 되게 된다.
자바 변수의 종류
- 클래스 변수 (클래스 영역에서 static 붙는 변수)
- 인스턴스 변수 (클래스 영역에서 static 아닌 변수, 인스턴스 더이상 참조되지 않으면 GC에서 처리함.)
- 지역 변수 (메서드 내에서 선언되고 메서드 수행 끝나면 소멸되는 변수)
- 매개 변수 ( 메서드 호출 시 '전달하는 값을 가지고 있고, 선언된 부분 부터 수행이 끝날때까지 유효함)
각 변수의 생성 시기
클래스변수 : 클래스가 메모리에 올라갈 때
인스턴스변수 : 인스턴스가 생성되었을 때
지역변수 / 매개변수 : 위치하고 있는 메서드가 수행되었을 때
Method(static) 영역
- JVM이 동작해서 클래스가 로딩될 때 생성.
- JVM이 읽어들인 클래스와 인터페이스 대한 런타임 상수 풀, 멤버 변수(필드), 클래스 변수(Static 변수), 상수(final), 생성자(constructor)와 메소드(method) 등을 저장하는 공간.
- Method(Static) 영역에 있는 것은 어느곳에서나 접근 가능
- Method(Static) 영역의 데이터는 프로그램의 시작부터 종료가 될 때까지 메모리에 남아있다. 그래서 static 메모리에 있는 데이터들은 프로그램이 종료될 때까지 어디서든 사용이 가능하다.그러나 static 데이터를 무분별하게 많이 사용할 경우 메모리 부족 현상이 일어날수 있게 된다.
Stack 영역
- 메소드 내에서 정의하는 기본 자료형에 해당되는 지역변수의 데이터 값이 저장되는 공간
- 메소드가 호출될때 스택 영역에 스택 프레임이 생기고 그안에 메소드를 호출
- primitive 타입의 데이터(int, double, byte, long, boolean 등) 에 해당되는 지역변수, 매개 변수 데이터 값이 저장
- 메소드가 호출 될 때 메모리에 할당되고 종료되면 메모리에서 사라짐
Stack 은 후입선출 LIFO(Last-In-First-Out) 의 특성을 가지며, 스코프(Scope) 의 범위를 벗어나면 스택 메모리에서 사라진다.
스택 프레임(stack frame)
하나의 메서드에 필요한 메모리 덩어리를 묶어서 스택 프레임
이라고 한다.
하나의 메서드 당 하나의 스택 프레임이 필요하며, 메서드를 호출하기 직전 스택 프레임을 자바 Stack 에 생성하고 메서드를 호출한다.
메서드의 매개 변수, 지역변수, 리턴값을 스택 프레임에 쌓는다.
메서드 호출범위가 종료되면 스택에서 제거된다.(가장 최근에 쌓였으니, 삭제하기도 편할듯)
Heap 영역
- JVM이 관리하는 프로그램 상에서 데이터를 저장하기 위해 런타임 시 동적으로 할당하여 사용하는 영역
- 참조형(Reference Type) 데이터 타입을 갖는 객체(인스턴스), 배열 등이 저장 되는 공간
- 단, Heap 영역에 있는 오브젝트들을 가리키는 레퍼런스 변수는 stack에 적재
- Heap 영역은 Stack 영역과 다르게 보관되는 메모리가 호출이 끝나더라도 삭제되지 않고 유지된다.그러다 어떤 참조 변수도 Heap 영역에 있는 인스턴스를 참조하지 않게 된다면, GC(가비지 컬렉터)에 의해 메모리에서 청소된다. => 예시는 상세하나 잘 이해되지 않음.
- stack은 스레드 갯수마다 각각 생성되지만, heap은 몇개의 스레드가 존재하든 상관없이 단 하나의 heap 영역만 존재
Heap과 Stack의 차이점
- 힙 메모리는 애플리케이션의 모든 부분에서 사용되며, 반면에 스택 메모리는 하나의 스레드가 실행될 때 사용. 그래서 힙 과 메서드 공간에 저장된 객체는 어디서든지 접근이 가능하지만, 스택 메모리는 다른 스레드가 접근할 수 없다.
- 언제든지 객체가 생성되면 항상 힙 공간에 저장되며, 스택 메모리는 힙 공간에 있는 객체를 참조만 한다.즉, 스택 메모리는 primitive 타입의 지역변수와 힙 공간에 있는 객체 참조 변수만 갖고 있다.
- 스택메모리의 생명주기는 매우 짧으며, 힙 메모리는 애플리케이션의 시작부터 끝까지 살아남는다.
- 자바 코드를 실행할때 따로 -Xms과 -Xmx 옵션을 사용하면 힙 메모리의 초기 사이즈와 최대 사이즈를 조절할 수 있다.
- 스택 메모리가 가득차면 자바에서는 java.lang.StackOverFlowError를 발생.힙 메모리가 가득차면 java.lang.OutOfMemoryError : Java Heap Space 에러를 발생
- 스택 메모리 사이즈는 힙 메모리와 비교했을 때 매우 적다. 하지만 스택 메모리는 간단한 메모리 할당 방법(LIFO)를 사용하므로 힙 메모리보다 빠르다. ==> 추가 질문: 힙메모리는 어떤 메모리 할당방법을 사용할까?
출처:
|
||
@PostMapping("/reservations") | ||
public ResponseEntity<Reservation> create(@RequestBody Reservation reservation) { | ||
if (Objects.isNull(reservation.getDate()) || reservation.getDate().isEmpty() || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
예외처리를 꼼꼼히 하셨군요!!
요렇게 할 수도 있지만, 자바의 @Valid를 활용해 볼 수 있습니다.
한번 찾아보시고 학습해보시는 걸 추천드려요
throw new BadRequestException("올바르지 않은 입력입니다."); | ||
} | ||
|
||
SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate).withTableName( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SimpleJdbcInsert도 익히셨군요. 좋습니다.
지금 SimpleJdbcInsert 객체가 요청이 들어올때마다 생성이 되는 것 같아요.
어떻게 하면 이런 객체의 중복생성을 줄일 수 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
생성자 주입을 받는 방법을 사용할것 같습니다
SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate).withTableName( | ||
"reservation").usingGeneratedKeyColumns("id"); | ||
|
||
Map<String, Object> map = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
map에서 put으로 계속 만들 수도 있지만, Map.of로 한번에 생성괴 동시에 초기화도 할 수 있습니다.
한번 찾아보시는 걸 추천드려요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
감사합니다.
10개가 넘어가는 경우, 다른 방법을 택해야 하는 것(put을 쓰거나, ofEntries 사용) 도 확인했습니다.
} | ||
|
||
@PostMapping("/reservations") | ||
public ResponseEntity<Reservation> create(@RequestBody Reservation reservation) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
지금 도메인 객체를 그대로 파라미터로 받고 있지만, 이는 view와 도메인이 강결합된 형태에요.
어떻게 하면 이를 분리 할 수 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DTO를 사용해서 중간 계층을 하나 추가할 수 있을것 같습니다
|
||
@DeleteMapping("/reservations/{id}") | ||
public ResponseEntity<Void> delete(@PathVariable Long id) { | ||
int count = this.jdbcTemplate.queryForObject( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
어떤 jdbcTemplate엔 this가 붙고 어디엔 안 붙는 군요. 성재님만의 기준이 있으신가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this가 생략 가능한지 몰랐는데, 생략이 되고 있었군용..
this 키워드에 대한 추가적인 조사를 해볼 수 있었습니다.
throw new BadRequestException("존재하지 않는 예약입니다."); | ||
} | ||
|
||
this.jdbcTemplate.update("delete from reservation where id = ?", id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
객체를 조회한 뒤에, delete 쿼리를 날리는 군요. 이 경우 쿼리가 두번 나갈 거 같아요.
어떻게 하면 쿼리를 줄이면서, 예외처리도 할 수 있을까요?
update 메서드의 반환값은 뭔가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- store시 RequestBody를 Map으로 받아봄
- Time 관련 엔티티 매핑 - RequestBody 값을 바로 Reservation에 매핑할 수 없으므로 RequestDto 사용 - 테스트 코드에서 time 관련 오류가 발생하지 않도록 테스트 코드 수정
8~10 단계 진행시 생긴 질문사항
|
미션 진행 시 추가 학습하고 싶은 내용
1. store, update 등의 컨트롤러 로직에서 파라미터를 사용하는 방법
update나 create 시 에 Controller에서
@RequestBody
로 특정 모델을 주입 받는데,등등 파라미터를 처리하는 방법이 궁금해졌습니다.
2. AtomicLong, Long, Void 등등, 새로 등장한 클래스 들에 대한 학습
3. Spring에서 웹요청에 대한 Test를 진행하는 방법들에 대한 상세학습