저번시간에는 비즈니스 요구 사항 정리를 통해서 어떤식으로 진행할지 먼저 알아봤다.
이번시간에는 도메인과 레포지토리를 만들어보자.
📝 회원 관리 비즈니스 요구사항
- 데이터 : 회원 ID, 이름
- 기능 : 회원등록, 조회
- DB 저장소는 구현체(인터페이스를 구현한 클래스)로 메모리 기반 데이터 저장소 사용
📚 회원 도메인 만들기
domain이라는 package를 만들고 Member 클래스를 생성하여 회원정보를 담을 변수를 선언했다.
또한 비즈니스 요구사항 중 필요한 데이터는 회원 ID, 이름로 아래와 같이 코드를 작성하자.
public class Member {
private Long id;
private String name;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
📚 회원 레포지토리 인터페이스 만들기
repository(저장소)라는 package를 하나 생성해서.
회원 객체를 저장하는 저장소를 만들고 이후 인터페이스 MemberRepository를 생성할 예정이다.
우리가 작성한 시나리오가 현재 데이터 베이스(DB)가 선정되지 않은 상황이기 때문에,
메모리 기반의 데이터 저장소를 사용하기로 했다.
음... 인터페이스라는 걸 배웠긴 했지만, 좀 오래 되었으니 인터페이스에 대해서 좀만 짚고 가자.
아래 포스팅을 혹시 인터페이스가 뭐였지? 라는 생각이 든다면 아래 포스팅 봐주세요!
[개발지식] 모호한 개념인 인터페이스를 쉽게 정리해보자 ! 인터페이스란 무엇인가 ?
국비학원 다니면서도 인터페이스에 대해서 배웠지만, 그때 당시에는 '이걸 왜 쓰지...? '라는 물음표 ????를 남긴 채 계속 사용했었다. 그러다가 스프링입문 강의를 들으면서, 인터페이스를 쓰게
jnaa.tistory.com
public interface MemberRepository {
Member save(Member member); // 1. 저장
Optional<Member> findById(Long id); // 2. ID를 통해서 member 찾기
Optional<Member> findByName(String name); // 3. 사용자 이름을 통해 member 찾기
List<Member> findAll(); // 4. 저장된 모든 회원 리스트 반환
}
- Save (저장)
- member에 들어갈 정보(id, name)을 받았을 때, 저장소에 저장 - findById
- 해당 ID를 가지고 있는 member를 찾는 기능 - findByName
- 사용자 이름을 받아서, 해당 이름을 갖고 있는 member를 찾는 기능 - findAll
- 지금까지 저장된 모든 회원 리스트를 반환하는 기능
📚 구현체 만들기
구현체가 상속할 interface를 만들었으니 상속 받을 구현체를 만들어보자
repository package 안에 MemoryMemberRepository 클래스를 생성하고 interface를 상속받는다.
public class MemoryMemberRepository implements MemberRepository{
@Override
public Member save(Member member) { }
@Override
public Optional<Member> findById(Long id) { }
@Override
public Optional<Member> findByName(String name) { }
@Override
public List<Member> findAll() { }
}
member들 정보를 save할 때 저장할 장소가 필요하니 Map을 이용해서 만들어보자
또한 회원의 정보를 저장할 때 id를 부여하고 새로운 회원객체를 생성할때 마다 시퀀스를 주도록 만들었다.
private static Map<Long, Member> store = new HashMap<>(); // member 객체 저장
private static long sequence = 0L; // 시스템이 부여하는 ID 번호
이제 오바라이드 된 메서드를 구현해보자 !
1. save
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
save는 Map에 member 객체를 저장하고, 저장한 객체를 반환하는 함수이다.
member 객체의 변수에는 id와 name 두 개가 있었는데, name은 사용자에게 이미 입력을 받은 상태라고 가정하자.
1. member.setId(++sequence)
- Member 객체의 setId 메서드를 호출하여 멤버의 ID를 설정하고 ++sequence로 고유한 ID생성
2. store.put(member.getId(), member)
- member.getId()는 멤버 객체의 ID를 가져와서 이 ID를 키로 사용하여 멤버 객체를 store에 저장
3. return member
- 저장된 member 객체를 반환
2. findById
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
}
주어진 ID에 해당하는 멤버를 찾는 메서드 이다.
1. Optional.ofNullable(store.get(id))
- store에서 해당 ID를 가진 멤버를 검색합니다. 그 결과를 Optional.ofNullable을 사용하여 Optional 객체로 감싸준다
2. return Optional<Member>
- Optional 객체를 반환
🤔 Optional 이란 ?
NPE 발생을 방지하기 위해 사용하는 것으로
메서드가 반환할 결과값이 ‘없음’을 명백하게 표현할 필요가 있고, null을 반환하면 에러를 유발할 가능성이 높은 상황에서 메서드의 반환 타입으로 Optional을 사용하자는 것이 Optional을 만든 주된 목적이다.
3. findByName
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
member이름을 사용하여 특정이름을 가진 멤버를 찾는 메서드이다.
1. store.values().stream()
- 멤버 목록을 처리 할 수 있게 한다.
2. . filter(member -> member.getName().equals(name))
- filter 연산을 사용하여 조건에 만족하는 name과 member에 name이 동일한지 비교한다.
3. .findAny()
- 필터링한 결과가 하나라도 있으면 그 결과를 Optional로 반환을 한다
오... 일단 지금까지 ' Optional 이 뭐지 ? 국비랑 다르잖아.. 이게 무슨소리야..? '라고 들었지만 계속 듣다보면 이해하겠지...
다음시간에는 구현한 기능이 작동하는지 Test코드를 확인해보자 !
'ON > 실습' 카테고리의 다른 글
[Spring Boot] 회원관리 예제 - 회원 서비스 개발 ④ (0) | 2023.11.13 |
---|---|
[Spring Boot] 회원관리 예제 - 회원 리포지토리 테스트 케이스 작성③ (0) | 2023.11.09 |
[Spring Boot] 회원관리 예제 - 비즈니스 요구 사항 정리 ① (0) | 2023.11.07 |
[Spring Boot] 스프링 웹 개발 기초 ③ - API (2) | 2023.11.06 |
[Spring Boot] 스프링 웹 개발 기초 ② - MVC와 템플릿 엔진 (1) | 2023.11.05 |