1. 회원 서비스 개발
public Long join(Member member) {
// 같은 이름이 있는 중복회원 X
//ctrl + alt + v 단축키로 자동으로 optional 입력해줌. 반환이 Optional
memberRepository.findByName(member.getName()).ifPresent(m -> { //ctrl + alt + n 메서드 체이닝 -> 변수 선언과 동시에 로직을 수행한 후 바로 리턴하는 구조, 변수 선언이 불필요
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
memberRepository.save(member);
return member.getId();
}
- findByName 이후 로직이 길게 나온다 -> method로 뽑는 것이 낫다. (Extract Method)
단축키를 사용할 수 있지만 어떤 단축키인지 몰라서.. 뽑아내고 싶은 코드를 드래그한 후 Extract Method로 뽑아줬다. (refactoring)
public Long join(Member member) {
// 같은 이름이 있는 중복회원 X
//ctrl + alt + v 단축키로 자동으로 optional 입력해줌. 반환이 Optional 멤버
validateDuplicateMember(member); // 중복 회원 검증
memberRepository.save(member); // 통과하면 데이터 저장
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName()).ifPresent(m -> { //ctrl + alt + n 메서드 체이닝 -> 변수 선언과 동시에 로직을 수행한 후 바로 리턴하는 구조, 변수 선언이 불필요
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
회원 서비스 비즈니스 로직을 모두 작성했다면, 잘 작동하는지 테스트를 해봐야하는데 그 때 유용하게 쓰이는 것이 ctrl + shift + T 단축키이다. 테스트 케이스를 직접 생성하는 방법도 있지만 단축키를 활용하면 더 쉽고 빠르게 테스트를 할 수 있다.
테스트 케이스를 작성하길 원하는 메서드를 선택하고 나면 기본적인 틀이 작성된 Test 클래스를 볼 수 있다.
@Test
void join() {
//given
Member noob = new Member(); // 새로운 회원 noob 생성
noob.setName("뉴비에요"); // 회원 noob의 이름을 뉴비에요라고 지정
//when
Long saveId = memberService.join(noob); //memberService에 noob 데이터 저장
//then
Member findMember = memberService.findOne(saveId).get(); // memberService에서 saveId로 조회한 데이터를 가져와 findMember 변수에 저장
assertThat(noob.getName()).isEqualTo(findMember.getName());//noob.getName()의 값과 findMember.getName()의 값이 같은지 검증
}
- given : 어떠한 상황(무언가가)이 주어지면 // 테스트 데이터 기반
- when : 이 메서드를 실행했을 때 // 검증하려는 부분
- then : 결과가 이러이러하게 나와야 한다 -> 검증부
TEST 코드를 작성할 때 given, when, then 세 구간으로 나눠 작성하면 아 이 데이터를 기반으로 이 부분을 검증하는구나, 여기가 검증부구나 라는 것을 더 쉽게 알아낼 수 있다.
강의를 들으면서 내가 이해한 내용이 맞는지 계속 챗GPT를 활용해 확인하는데, 그러다보니 코드가 다소 더러워보인다.
하지만 기록해놓지 않고 까먹는 것보다야 기록하는 것이 낫다고 생각해서 익숙해질 때까지는 계속 주석을 달아놓을 예정이다.
그리고 강의 내용에 나온 변수이름이 아니라 내가 지정한 변수 이름으로 변경해서 코드를 작성하다보니 메서드를 사용할 때 내가 가끔 다른 객체나 변수를 지정하고 있다는 사실을 알 수 있고, 무엇을 가리키는 것인지 확실하게 알 수 있게 되어서 좋다! (추천합니다!)
정상작동하는지 확인했으면 TEST 완료구나 생각했는데 사실 더 중요한 것은 따로 있었다.
바로 예외로 오류를 처리하는 TEST가 훨씬 중요하다는 것이다. (위 TEST는 반쪽짜리였다..)
@Test
public void duplicateMemberException() { // 중복회원 예외처리
//given
Member noob1 = new Member(); //이름이 같은 회원(중복)이 있을 경우의 예외 처리하기
noob1.setName("뉴비1");
Member noob2 = new Member();
noob2.setName("뉴비1");
//when
memberService.join(noob1);
Assertions.assertThrows(IllegalStateException.class, () -> memberService.join(noob2));
memberService.join(noob2);
//then
}
미리 MemberService 클래스에 throws new 로 처리해둔 예외 메시지가 출력되며 잘 작동하는 것을 확인했다.
noob1과 noob2 객체 모두 이름을 뉴비1 로 지정해도 내가 찾으려고 하는 이름 데이터가 noob1의 것이라면 이름이 같아도 중복예외처리가 되어야한다.
memberService.join(noob1);
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(noob2));
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
적상 작동하는 것을 확인했다!
2. 회원 컨트롤러가 회원서비스와 회원 레포지토리를 사용할 수 있게 의존관계를 준비
1) 자동 컴퍼넌트 스캔 이용하기 ( 컴포넌트 스캔 : Spring이 프로젝트를 스캔하며 객체를 생성하고 관리하는 과정)
Spring은 @Controller, @Service, @Repository 등의 애너테이션이 붙은 클래스를 자동으로 컴포넌트 스캔해 관리한다.
이렇게 등록된 객체들은 Spring Container (IOC Container)에 등록된다.
Controller에서 Service가 필요하면 Spring이 자동으로 Service를 주입하고, Service에서 Repository가 필요하면 Repository를 주입한다.
@Controller
public class MemberController {
private final MemberService memberService; //new 키워드로 생성을 하게되면 여러 클래스에서 MemberService를 갖다써야함. MemberService는 여러 인스턴스를 생성할 필요없이 하나만 만들어 공용으로 가져다 쓰는 것이 효율적
@Autowired // 자동으로 연결해줌
public MemberController(MemberService memberService) { // 생성자에서 이렇게 쓰면 Member Contrller가 생성이 될 때 Spring Bean에 등록되어있는 Member Service 객체를 갖다 넣어준다 (의존관계 주입)
this.memberService = memberService;
}
}
@Repository
public class MemoryMemberRepository implements MemberRepository
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired // AutoWired를 통한 의존성 주입은 스프링이 관리하는 객체에서만 동작하므로 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository; // 멤버 레포지토리를 직접 new로 생성하는 것이 아니라 외부에서 넣어주도록 constructor 설정
}
< 컴포넌트 스캔 방식 >
▶ 장점
- 반복적인 등록 작업을 줄여 코드를 간결하게 하고, 생산성을 향상한다.
- 패키지 내의 모든 빈을 자동으로 등록하여 관리해 편리하다.
- 기본 설정일 때 빠르게 구현 가능
▶ 단점
- 스캔 범위를 지정하지 않으면 불필요한 빈이 등록될 가능성이 있다.
- 의존 관계가 명시적이지 않아 자동 주입 실패 시 디버깅이 어렵다
▶ 사용하는 때
- 등록해야할 빈과 클래스 수가 많아 일일이 등록하는 것이 비효율적일 때
- 표준적인 빈 관리만 필요하고, 추가 설정이 복잡하지 않을 때
2) 자바 코드로 직접 스프링 빈(Spring Bean) 등록하기
package hello.hello_spring;
import hello.hello_spring.repository.MemberRepository;
import hello.hello_spring.repository.MemoryMemberRepository;
import hello.hello_spring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository()); // 생성자에 무엇을 넣어줘야 하는지 확인하려면 ctrl + p
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository(); // memberRepository를 넣으려고했나? 안된다. 왜냐하면 memberRepository는 인터페이스이기 때문이다. (구현체를 넣어야함)
}
}
< 자바 코드로 직접 등록 >
▶ 장점
- 빈 등록 위치가 명확하고 직관적이다.
- 의존관계를 명시적으로 정의 가능하기 때문에 의존성 주입 로직을 명확히 확인 가능하다.
- 비즈니스 로직이 복잡한 경우에 사용하면 효과적이다.
▶ 단점
- 많은 클래스를 등록할 경우 번거롭다.
- 빈의 의존 관계를 추가/변경할 때 Config 파일을 수정해야한다.
▶ 사용하는 때
- 특정 빈에 대해 세밀한 제어가 필요할 때
- 특정 의존성 주입 로직을 명시적으로 정의하고 싶을 때
- 빈의 생성 방식이나 초기화 작업이 복잡할 때
- 다른 외부 라이브러리와 함께 사용할 때 (Spring이 아닌 빈도 관리해야 할 때)
의존성 주입에는 필드 주입, setter 주입, 생성자 주입 세 가지 방법이 있으나 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
실무에서는 주로 정형화된 컨트롤러, 서비스 , 레포지토리같은 코드는 컴포넌트 스캔을 사용하고 정형화되지 않거나 상황에 따라 구현 클래스를 변경해야 할 때 설정을 통해 스프링 빈으로 등록한다.
Opinion
오늘은 듣다 만 테스트 섹션을 끝내고 드디어 기대했던 회원 서비스를 개발하는 강의를 들었는데 17분 짜리 수업을 들으면서 3시간을 썼다.
이해가 되지 않아서 여러 번 반복해서 듣고, 코드를 따라치다가 참조대상을 잘못 지정해 에러가 난 것을 찾아다니고, 여기저기 질문을 하다가 하루가 다 가버렸다. 내 코드로 인해 html이 변하는 것을 실시간으로 확인할 수도 없고 println으로 출력 결과를 볼 수도 없으니 수업을 듣는 내내 이해하기도 어렵고 재미도 없어서 혼났다.. Test를 할 땐 그래도 에러를 눈으로 보고 정상 작동 되는 것을 확인할 수 있어서 좋았는데
의존성 주입은 정말 어려운 것같다.. 하지만 이제 조금있으면 데이터베이스를 연결해서 사이트가 정말 구현되는 것을 배울 수 있으니
어떻게든 빨리 들어보려고 노력중이다.
얼른 강의를 듣고 과제를 진행해야지...!!!
'내일배움캠프 > TIL' 카테고리의 다른 글
[Spring_4기 본캠프] 메모장 실습과 Postman, 깃모지 | Day 35 (0) | 2024.12.04 |
---|---|
[Spring_4기 본캠프] Spring 입문 - 스케줄러 실습과 트러블슈팅 | Day 34 (0) | 2024.12.03 |
[Spring_4기 본캠프] Spring 입문 - 회원관리 예제 테스트해보기 | Day 32 (3) | 2024.12.01 |
[Spring_4기 본캠프] Spring 입문 - 백엔드 개발 | Day 31 (1) | 2024.11.29 |
[Spring_4기 본캠프] Spring 입문 - build terminal 오류 트러블 슈팅 | Day 30 (3) | 2024.11.28 |