개발지식

DI를 하지 않았을 때 NullPointerException 발생 이유

우루쾅 2024. 3. 22. 10:04
728x90
반응형
SMALL

이 게시글은 이동욱 - "스프링 부트와 AWS로 혼자 구현하는 웹 서비스" 에서 참고하였습니다.

 

위 책을 참고하여 JUnit 에서 값을 등록하는 테스트를 만들어보았습니다.

 

문제 상황

JUnit 을 수행하는 과정에서 posts api 까지 정상적으로 값이 전달되지만 NullPointerException 에러가 발생

 

■ PostsApiControllerTest.java

@Test
public void Posts_등록된다() throws Exception {
    //given
    String title = "title";
    String content = "content";
    PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
            .title(title)
            .content(content)
            .author("author")
            .build();

    String url = "http://localhost:" + port + "/api/v1/posts";

    //when
    ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDto, Long.class);

    //then
    List<Posts> all = postsRepository.findAll();
    assertThat(all.get(0).getTitle()).isEqualTo(title);
    assertThat(all.get(0).getContent()).isEqualTo(content);
}

 

■ PostsSaveRequestDto.java

@Getter
@NoArgsConstructor
public class PostsSaveRequestDto {
    private String title;
    private String content;
    private String author;
    @Builder
    public PostsSaveRequestDto(String title, String content, String author){
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public Posts toEntity(){
        return Posts.builder()
                .title(title)
                .content(content)
                .author(author)
                .build();
    }
}

 

■ PostsApiController.java

@RequiredArgsConstructor
@RestController
public class PostsApiController {

    private PostsService postsService;

    @PostMapping("/api/v1/posts")
    public Long save(@RequestBody PostsSaveRequestDto requestDto){
        return postsService.save(requestDto);
    }
}

 

위 코드를 베이스로 Test를 수행하면 아래와같은 NullPointerException 에러가 발생합니다.

 

 


문제 해결

해당 캐이스에 대해서 원인을 파악해보니 save 메서드에서 postsService를 사용할 때 외부에서 가져오는것이 아닌 PostsApiController 에서 새로 선언하여 가져왔기 때문에 null 값을 가지고 오고 있었습니다.

 

예를 들어, PostsApiController가 PostsService의 메소드를 호출하여 작업을 수행한다고 가정해봅시다. PostsApiController가 직접 PostsService의 인스턴스를 생성하면, 두 클래스 사이의 결합도가 높아집니다. 이로 인해 PostsService의 변경이 PostsApiController에도 영향을 미칠 수 있고, 테스트하기 어려워집니다.

의존성 주입을 사용하면, PostsService의 인스턴스를 PostsApiController 외부에서 생성하고 이를 PostsApiController에 주입합니다. 이렇게 하면 PostsApiController는 PostsService의 구현 세부 사항을 알 필요가 없으며, 다른 PostsService 구현으로 쉽게 교체할 수 있게 됩니다.

제공된 코드에서 PostsService 필드는 초기화되지 않았습니다.
즉, PostsService의 인스턴스가 생성되어 PostsApiController에 주입되지 않았기 때문에, postsService는 기본값인 null을 가집니다. 그런 다음 save 메소드에서 postsService.save(requestDto);를 호출하려고 하면, 실제로 null.save(requestDto);를 호출하려고 시도하는 것과 같습니다. 자바에서는 null에 대해 메소드를 호출하려고 하면 NullPointerException이 발생합니다.

 

수정된 코드

코드 수정 후 정상 동작 확인!

 

 

결론

NullPointerException 에러가 발생했을 때 데이터가 비어있는 부분을 찾지 못했다면 DI를 빼먹은 부분이 있는지 의심해보자!

 

728x90
반응형
LIST