Cộng đồng Hỏi & Đáp của Inflearn

Tạo máy chủ đầu tiên của bạn bằng Java và Spring Boot Bất kỳ ai cũng có thể dễ dàng chuyển từ giai đoạn phát triển sang triển khai! [Gói phát triển máy chủ tất cả trong một]
안녕하세요. 강의 후 개인적으로 학습 시 나타나는 NPE관련 질문드립니다.
Đã giải quyết
Viết
·
366
1
안녕하세요! 강의를 완강 후 혼자 프로젝트를 진행하다 도서 대출 코드를 보고 비슷하게 구현한 사용자가 채용공고를 지원하는 메소드를 호출시 테스트 코드에서 NPE가 발생하는데 혹시 이유를 알 수 있을까요? 여러가지 서칭해봐도 해결이 안되서 질문드립니다... ㅠㅠ
회원entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "users")
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ApplyHistory> applyHistory = new ArrayList<>();
public void applyCompany(JobPosting jobPosting) {
this.applyHistory.add(new ApplyHistory(this, jobPosting));
}
@Builder
private User(Long id, String name, List<ApplyHistory> applyHistory) {
this.id = id;
this.name = name;
this.applyHistory = applyHistory;
}
}ApplyHistory entity(JobPosting과 user객체가 N:M 매핑해주는 entity)
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "apply_history")
@Entity
public class ApplyHistory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "apply_history_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "job_posting_id")
private JobPosting jobPosting;
public ApplyHistory(User user, JobPosting jobPosting) {
this.user = user;
this.jobPosting = jobPosting;
}
}
JobPosting Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "job_posting")
@Entity
public class JobPosting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "job_posting_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "company_id")
private Company company;
@Column(name = "posting_position")
private String position;
private int compensation; //채용보상금
@Column(name = "posting_details")
private String postingDetails;
@Column(name = "technology_used")
private String technologyUsed;
@Builder
private JobPosting(Company company, String position, int compensation, String postingDetails,
String technologyUsed) {
this.company = company;
this.position = position;
this.compensation = compensation;
this.postingDetails = postingDetails;
this.technologyUsed = technologyUsed;
}
public void updateJobPosting(String position, int compensation, String postingDetails,
String technologyUsed) {
this.position = position;
this.compensation = compensation;
this.postingDetails = postingDetails;
this.technologyUsed = technologyUsed;
}
}
applyService
@RequiredArgsConstructor
@Service
public class ApplyService {
private final JobPostingRepository jobPostingRepository;
private final UserRepository userRepository;
private final ApplyHistoryRepository userJobPostingRepository;
@Transactional
public void applyCompany(ApplyCompanyRequest request) {
// 1. 채용공고 정보 찾기
JobPosting jobPosting = jobPostingRepository.findById(request.getJobPostingId())
.orElseThrow(() -> new ResourceNotFoundException("jobPosting", request.getJobPostingId()));
// 2. 유저 정보 가져오기
User user = userRepository.findById(request.getUserId())
.orElseThrow(() -> new ResourceNotFoundException("user", request.getUserId()));
// 3. 지원 유무 확인
// 3-1. 지원 중이면 예외 발생
if (userJobPostingRepository.existsByJobPostingAndUser(jobPosting, user)) {
throw new IllegalArgumentException("이미 지원하신 회사입니다.");
}
user.applyCompany(jobPosting);
}
}
리퀘스트
@Getter
@Setter
public class ApplyCompanyRequest {
private Long jobPostingId;
private Long userId;
}
서비스 테스트 코드
@SpringBootTest
class ApplyServiceTest {
@Autowired
JobPostingService jobPostingService;
@Autowired
ApplyService applyService;
@Autowired
JobPostingRepository jobPostingRepository;
@Autowired
UserRepository userRepository;
@Autowired
ApplyHistoryRepository applyHistoryRepository;
@Autowired
CompanyRepository companyRepository;
@AfterEach
void tearDown() {
applyHistoryRepository.deleteAllInBatch();
jobPostingRepository.deleteAllInBatch();
userRepository.deleteAllInBatch();
companyRepository.deleteAllInBatch();
}
@DisplayName("사용자는 채용 공고를 지원 할 수 있다.")
@Test
@Transactional
void applyCompany() {
//given
User user = User.builder()
.id(1L)
.name("jw")
.build();
User savedUser = userRepository.save(user);
Company company = Company.builder()
.name("company1")
.country(Country.KOREA)
.city(City.SEOUL)
.build();
Company savedCompany = companyRepository.save(company);
JobPosting jobPosting = JobPosting.builder()
.company(savedCompany)
.position("백엔드")
.postingDetails("백엔드 개발자 채용합니다.")
.compensation(500000)
.technologyUsed("Java")
.build();
JobPosting savedJobPosting = jobPostingRepository.save(jobPosting);
ApplyCompanyRequest request = new ApplyCompanyRequest();
request.setUserId(savedUser.getId());
request.setJobPostingId(savedJobPosting.getId());
//when
applyService.applyCompany(request);
//then
}
}
-> 이부분에서 applyCompany(request) 호출 시 NPE가 발생합니다.
java.lang.NullPointerException
at com.wanted.findjob.domain.user.User.applyCompany(User.java:36)
at com.wanted.findjob.api.service.ApplyService.applyCompany(ApplyService.java:39)
at com.wanted.findjob.api.service.ApplyService$$FastClassBySpringCGLIB$$2f4064b0.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:792)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:707)
at com.wanted.findjob.api.service.ApplyService$$EnhancerBySpringCGLIB$$81701d47.applyCompany(<generated>)
at com.wanted.findjob.api.service.ApplyServiceTest.applyCompany(ApplyServiceTest.java:83)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
테스트코드가 아닌 직접 서버를 작동해서 api를 호출 시 정상적으로 db에 들어가는 걸 볼 수 있는데 어디가 문제 인지를 모르겠습니다.. ㅠ
Câu trả lời 2
1
User Entity에서 Bulider패턴 사용시 List<ApplyHistory> applyHistory 초기화 부분문제 때문에 일어난 거였습니다.. 혹시라도 비슷한 경험을 하시는 분들을 위해 질문은 남겨 놓을게요 감사합니다!
0
안녕하세요 정우님!! 와우~ 해결하셨군요!! 다행입니다~ 😊
추가적으로 테스트를 하다가 뭔가 동작이 이상하면
중간에 System.out.println()을 사용하시거나 (약간 비추천)
IntelliJ 디버깅 기능을 활용하셔서 (매우 추천)
DB의 데이터를 확인해보실 수 있습니다.
예를 들어, 테스트 중간에 ApplyHistoryRepository에 접근해보는 것이죠!
혹시나 또 어려운 점 있으시면 편하게 질문 남겨주세요.
감사합니다! 🙇





친절한 답변에 링크 영상까지 정말 감사합니다.!!