Skip to content
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

Open
wants to merge 30 commits into
base: bingle625
Choose a base branch
from

Conversation

bingle625
Copy link

미션 진행 시 추가 학습하고 싶은 내용

1. store, update 등의 컨트롤러 로직에서 파라미터를 사용하는 방법

update나 create 시 에 Controller에서 @RequestBody 로 특정 모델을 주입 받는데,

  1. 파라미터값이 모델의 속성으로 매핑되는 흐름
  2. 모델이 제대로 생성되지 않는 경우의 수 (키값이 제대로 된게 하나도 없을 때 오류가 발생?),
  3. 여러 모델에 대한 파라미터를 한번에 입력받을 때

등등 파라미터를 처리하는 방법이 궁금해졌습니다.

2. AtomicLong, Long, Void 등등, 새로 등장한 클래스 들에 대한 학습

3. Spring에서 웹요청에 대한 Test를 진행하는 방법들에 대한 상세학습

Copy link

@hong-sile hong-sile left a 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'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

타임리프를 추가하셨군요! 이번 미션에선 사용 안한 것 같은데, 어떤 이유로 쓰셨나요?

Copy link
Author

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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

자바엔 long과 Long이 있습니다. 둘은 무엇이 다르고 각각 어떤 장단점이 있나요?

Copy link
Author

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)

image
  • Method(static) 영역
  • Stack 영역
  • Heap 영역
    데이터 타입(자료형)에 따라 각 영역에 나눠서 할당 되게 된다.

자바 변수의 종류

  1. 클래스 변수 (클래스 영역에서 static 붙는 변수)
  2. 인스턴스 변수 (클래스 영역에서 static 아닌 변수, 인스턴스 더이상 참조되지 않으면 GC에서 처리함.)
  3. 지역 변수 (메서드 내에서 선언되고 메서드 수행 끝나면 소멸되는 변수)
  4. 매개 변수 ( 메서드 호출 시 '전달하는 값을 가지고 있고, 선언된 부분 부터 수행이 끝날때까지 유효함)

각 변수의 생성 시기
클래스변수 : 클래스가 메모리에 올라갈 때
인스턴스변수 : 인스턴스가 생성되었을 때
지역변수 / 매개변수 : 위치하고 있는 메서드가 수행되었을 때

Method(static) 영역

  • JVM이 동작해서 클래스가 로딩될 때 생성.
  • JVM이 읽어들인 클래스와 인터페이스 대한 런타임 상수 풀, 멤버 변수(필드), 클래스 변수(Static 변수), 상수(final), 생성자(constructor)와 메소드(method) 등을 저장하는 공간.
  • Method(Static) 영역에 있는 것은 어느곳에서나 접근 가능
  • Method(Static) 영역의 데이터는 프로그램의 시작부터 종료가 될 때까지 메모리에 남아있다. 그래서 static 메모리에 있는 데이터들은 프로그램이 종료될 때까지 어디서든 사용이 가능하다.그러나 static 데이터를 무분별하게 많이 사용할 경우 메모리 부족 현상이 일어날수 있게 된다.
image

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() ||

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(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SimpleJdbcInsert도 익히셨군요. 좋습니다.

지금 SimpleJdbcInsert 객체가 요청이 들어올때마다 생성이 되는 것 같아요.
어떻게 하면 이런 객체의 중복생성을 줄일 수 있을까요?

Copy link
Author

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<>();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map에서 put으로 계속 만들 수도 있지만, Map.of로 한번에 생성괴 동시에 초기화도 할 수 있습니다.

한번 찾아보시는 걸 추천드려요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다.
10개가 넘어가는 경우, 다른 방법을 택해야 하는 것(put을 쓰거나, ofEntries 사용) 도 확인했습니다.

https://velog.io/@sangwoo0727/Map.of-%EB%A5%BC-%ED%86%B5%ED%95%9C-Map-%EC%B4%88%EA%B8%B0%ED%99%94-%EC%A3%BC%EC%9D%98%ED%95%A0-%EC%A0%90

}

@PostMapping("/reservations")
public ResponseEntity<Reservation> create(@RequestBody Reservation reservation) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금 도메인 객체를 그대로 파라미터로 받고 있지만, 이는 view와 도메인이 강결합된 형태에요.
어떻게 하면 이를 분리 할 수 있을까요?

Copy link
Author

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(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 jdbcTemplate엔 this가 붙고 어디엔 안 붙는 군요. 성재님만의 기준이 있으신가요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this가 생략 가능한지 몰랐는데, 생략이 되고 있었군용..
this 키워드에 대한 추가적인 조사를 해볼 수 있었습니다.

https://rorobong.tistory.com/122

throw new BadRequestException("존재하지 않는 예약입니다.");
}

this.jdbcTemplate.update("delete from reservation where id = ?", id);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

객체를 조회한 뒤에, delete 쿼리를 날리는 군요. 이 경우 쿼리가 두번 나갈 거 같아요.

어떻게 하면 쿼리를 줄이면서, 예외처리도 할 수 있을까요?
update 메서드의 반환값은 뭔가요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

영향 받는 row의 개수가 0인점을 이용하면 바로 예외처리를 할 수 있겠네요.
몰랐던 사실인데, 감사합니다.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html#update(java.lang.String)

@bingle625
Copy link
Author

8~10 단계 진행시 생긴 질문사항

  • [Heap영역에 관한 질문] Heap 영역은 Stack 영역과 다르게 보관되는 메모리가 호출이 끝나더라도 삭제되지 않고 유지된다.그러다 어떤 참조 변수도 Heap 영역에 있는 인스턴스를 참조하지 않게 된다면, GC(가비지 컬렉터)에 의해 메모리에서 청소된다. => 예시는 상세하나 잘 이해되지 않습니다.

  • [Setter Injection에 대한 질문] Setter Injection 방식은 메서드명에 관계없이, 파라미터, set하는 로직, autowired만 있으면 되는지 질문드립니다.

  • [Compnent Scan 학습테스트] 학습 테스트 > 3.1 @Compnent Scan 부분에 대해서 좀더 자세히 알고 싶습니다.

    • 어떤 방식으로 전개된건지 잘 이해가 되지 않습니다.
    • ++ 어노테이션 원리와, autowired의 원리도 궁금합니다.
  • record로 dto 만들었을 때 dto에서의 request validate 처리 방법

    • 저번에 보여주셨던거 같은데, null 필드에 대한 Exception을 어떻게 처리하면 좋을지, 어떤 키워드로 검색하면 정보를 얻을 수 있을지 질문드립니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants