멘지의 기록장

스프링 오답노트 본문

SpringBoot

스프링 오답노트

멘지 2024. 4. 5. 21:35

1) 처음에 postman으로 값을 넘겼는데 Invalid URI 에러가 나서 왜지? 했는데

http://localhost:8080

이거를 API 앞에 안붙였더라구요..ㅎㅎ

 

2)

ERROR 14664 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.RuntimeException] with root cause

java.lang.RuntimeException: null
	at java.base/java.util.Optional.orElseThrow(Optional.java:403) ~[na:na]
	at project.maribo.application.PostService.createPost(PostService.java:27) ~[main/:na]

위와 같은 오류가 나와서 응? 했습니다.

서버 재실행을 해도 안나오길래 왜지? 했는데 application.yml 파일에서

  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        format_sql: true
    show_sql: true

update가 아니라 create로 되어 있어서 서버 재실행 할때마다 DB를 밀고 다시 생성해서 그랬답니다..

 

3) 게시글 수정 코드를 작성했는데 postman에서도 200 처리가 났지만 DB에는 적용이 안되서 또 왜지? 가 되었습니다.

@Transactional

맨 위에 readOnly = true를 해놓아서 위의 validaion을 붙이지 않으면 수정이 안되는걸 깜빡했어요..휴

 

4) 이전까지 잘 돌아가던 코드들이 갑자기 안돌아갔어요.. 왜 그랬을까 찾아보니 Build를 Gradle -> IntelliJ로 변경하니까 문제가 있었나봐요. 조금 느리더라도 Gradle을 써야될거 같아요 :)

 

5) Enum 파일을 만들고 나서 값을 넣지 않으면 

query가 만들어질 때 위의 쿼리처럼 값이 넘어가야 하는데, 밑의 쿼리처럼 값이 넘어가지 않아 image_quiz 테이블이 안만들어지는 상황이 생긴답니다..!

 

6)

DTO를 record로 만들고서 DTO를 Entity로 만들 때 toEntity를 사용합니다.

근데 toEntity를 왜 static으로 만들지 않을까요?

➡️프론트에서 요청을 보낼 때 Controller에서 DTO에 값을 넣고, 해당 값을 Entity로 만들게 됩니다. static은 class에 의존적이기 때문에 객체가 생성되었을 때 사용이 되어야 하는 즉, 객체에 의존적이어야 하는 DTO와는 맞지 않게 되기 때문입니다!

 

7)

일반적으로 findById와 같이 Repository에서 값을 찾게 됩니다.

하지만 특이하게 Member_Role처럼 '_'를 작성한 것을 볼 수 있는데 그 이유는!

현재는 ImageQuizRepository이기 때문에 Role을 가지고 있지 않습니다.

그렇기에 Role을 찾기 위해선 MemberRepository에서 찾아야 하기 때문에 해당 Repository에 한번 더 들어가야 하는 경우에 사용합니다.

 

8) 

위의 @Query 문에서 ":"를 볼 수 있습니다.

아래의 @Param으로 받은 값을 그대로 넣어주겠다는 의미입니다!

잘 알고 있어야 될 것 같네요..ㅎㅎ

 

9) static import & 정적 초기화 블락 & Reflection

import static soongsil.kidbean.server.member.domain.type.Gender.MAN;

import org.springframework.test.util.ReflectionTestUtils;
import soongsil.kidbean.server.member.domain.Member;

public class MemberFixture {

    public final static Member MEMBER = Member.builder()
            .gender(MAN)
            .name("asd")
            .score(21L)
            .email("asd@naver.com")
            .build();

    static {
        ReflectionTestUtils.setField(MEMBER, "memberId", 1L);
    }
}

맨 위의 코드를 보면 import static 으로 되어 있는 것을 볼 수 있다.

왜 그냥 import가 아닌 static을 붙여준 것일까?

➡️ static import는 일반적인 import와는 다르게 메소드나 변수를 패캐지, 클래스명없이 접근가능하게 해줍니다.

 

정적 초기화 블락

아래에서 static {}으로 memberId를 MEMBER 객체에 넣어주는 것을 따로 뺀 것을 볼 수 있다.

 

정적 초기화 블락 : 클래스가 로딩될 때 호출되며 각 클래스 당 최1번만 실행된다.

static 블럭은 객체가 생성되기 이전에 수행되므로 인스턴스 멤버에 접근할 수 없다.

➡️ Static 안에 Non-static이 들어가면 안된다. (Static은 로딩될 때 생성되는데 Non-static이 있으면 생성할 수 없음.)

 

그렇다면 정적 초기화 블락은 왜 썼을까?

1. MEMBER 객체의 생성자에 memberId가 없어서 builder를 사용해서 memberId에 접근할 수 있는 방법이 없음.

2. ReflectionTestUtils.setField(MEMBER, "memberId", 1L); 는 실행문이기 때문에 따로 빼줘야 함

=> 위와 같은 2가지의 이유로 정적 초기화 블락을 사용

 

Reflection

MEMBER 객체의 private 필드인 memberId에 접근하기 위해 사용

원래 private은 해당 클래스 이외에는 사용이 불가하지만 Reflection은 private에도 접근하게 해줌

 

10) Annotation

@LocalDummyInit 어노테이션을 따로 생성해준 이유

➡️ 공통되는 Annotation이기 때문에

➡️ 모든 것을 필요한 곳에 다 붙이면 Annotation이 너무 많아짐

 

@Order(숫자)

어떤 Initializer가 먼저 생성될 지를 지정해주는 Annotation

 

11) Test 사용법

현재 Test 환경에서는 Mock을 사용해서 실제 Repository가 아닌 가상의 Repository를 만듦

그래서 Repository에 값이 들어가 있지 않기 때문에 Optional.of()를 사용해서 확인하고자 하는 값을 미리 넣어놓는다.