본문 바로가기

Programming/Spring

[Spring Boot] @RequiredArgsConstructor 사용 시 final 키워드 사용하지 않아서 에러가 발생한 것 같은데..

반응형

게시판 구현 연습 중 다음과 같은 에러와 마주했다.

java.lang.NullPointerException: Cannot invoke "com.changon.springboot.domain.posts.PostsRepository.findAllDesc()" because "this.postsRepository" is null
at com.changon.springboot.service.posts.PostsService.findAllDesc(PostsService.java:46) ~[classes/:na]
at com.changon.springboot.web.IndexController.index(IndexController.java:17) ~[classes/:na]

findAllDesc() 메소드는 게시판 전체 글 목록을 가져오는 메소드로서 메인 화면에 출력된다.

에러는 PostsRepository 클래스에 정의된 findAllDesc() 메소드 값이 null로 리턴되어 NullPointerException이 발생한 것 같다.

 

PostsRepository.java

package com.changon.springboot.domain.posts;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface PostsRepository extends JpaRepository<Posts, Long> {

    @Query("SELECT p FROM Posts p ORDER BY p.id DESC")
    List<Posts> findAllDesc();
}

 

에러 메시지에 PostsService 클래스 46번 라인, IndexController 클래스 17번 라인에 문제가 발생했다고 한다.

 

PostsService.java

@RequiredArgsConstructor
@Service
public class PostsService {

    private PostsRepository postsRepository;
    
    @org.springframework.transaction.annotation.Transactional(readOnly = true)
    public List<PostsListResponseDto> findAllDesc(){
// 46번 라인 return postsRepository.findAllDesc().stream().map(PostsListResponseDto::new).collect(Collectors.toList()); 
    }
}

IndexController.java

@RequiredArgsConstructor
@Controller
public class IndexController {

    private final PostsService postsService;

    @GetMapping("/")
    public String index(Model model){
 // 17번 라인 model.addAttribute("posts", postsService.findAllDesc()); 
        return "index";
    }
 }

결론부터 말하자면, PostsService 클래스에 생성자로 선언된 postsRepository에 final 키워드를 붙이지 않았기 때문에 에러가 발생했다.

private PostsRepository postsRepository;

이것을

private final PostsRepository postsRepository;

이렇게 수정하면 된다.

 

PostsService 클래스에 @RequiredArgsConstructor 어노테이션이 사용되었는데,

 

@RequiredArgsConstructor

  • 초기화 되지않은 final 필드, @NonNull이 붙은 필드를 매개변수로 갖는 생성자를 자동으로 생성한다.
  • 주로 의존성 주입(DI) 편의성을 높이기 위해 사용된다.

 

해당 내용을 바탕으로 본인이 해석한 내용은 다음과 같다.

@RequiredArgsConstructor 어노테이션 설정에 따라 생성자로 사용할 클래스 접근제어자 앞에 final 키워드를 추가하지 않아서 생성자가 만들어지지 않았고, 따라서 호출된 메소드에 대한 필드값이 들어가지 않았기 때문에 null값이 리턴되지 않았나 생각한다.

 

아직 명확하게 이해되지 않는다. final 키워드는 정확히 어떤 역할을 수행하는지, 그래서 생성자는 뭐였더라? 등등 공부할 내용이 많이 생겼다. 지식을 보완하여 좀 더 명확히 이해되는 대로 틈틈이 내용을 수정해야겠다.


참고

 

https://velog.io/@agugu95/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%8C%A8%ED%84%B4%EA%B3%BC-DAO-DTO-Repository

 

스프링과 DAO, DTO, Repository, Entity

스프링을 사용한 웹앱의 경우 DAO, DTO, Entity를 사용하여 데이터를 다룬다.

velog.io

https://yaboong.github.io/spring/2019/08/29/why-field-injection-is-bad/#:~:text=final%20%EC%9D%98%20%EC%9E%A5%EC%A0%90%EC%9D%80%20%EB%88%84%EA%B5%B0%EA%B0%80,%EA%B3%BC%20%EC%9C%A0%EC%82%AC%ED%95%9C%20%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C%20%EC%9D%B4%EB%A3%A8%EC%96%B4%EC%A7%84%EB%8B%A4.

 

스프링 - 생성자 주입을 사용해야 하는 이유, 필드인젝션이 좋지 않은 이유

개요 Dependency Injection (의존관계 주입) 이란 Setter Based Injection (수정자를 통한 주입) Constructor based Injection (생성자를 통한 주입) 스프링에서 사용할 수 있는 DI 방법 세가지 생성자 주입을 이용한 순

yaboong.github.io

 

반응형