2장에 이어서 예제 요구에 대한 백엔드를 마저 구성해보겠습니다.
공부 자료는 인프런 김영한 강사님의 스프링 입문편을 들었습니다.
https://inf.run/Jk5T
회원 도메인 생성
2장에서 설명했듯이, 도메인은 회원 객체라고 생각하시면 됩니다.
예제에서 회원 ID와 이름 데이터를 요구하였기 때문에 이에 맞게 생성하시면 됩니다.
먼저 위와 같이 도메인 패키지를 먼저 생성한 후, 아래와 같이 Member 클래스를 작성해줍니다.
package com.example.demo.domain;
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;
}
}
Member 클래스를 생성한 후, 본인이 아니면 id와 이름에 접근할 수 없게 private 로 id와 name 변수를 선언해 줍니다.
이 때 name 은 사용자가 지정하는 email, nickname 과 같은 문자열 변수이며, id 는 회원 등록시 임의로 생성되는 정수형 변수를 의미합니다.
두 변수를 선언 해주었다면, IntellJ 에서는 Ctrl + n 단축키를 사용해 두 변수에 대한 get, set 메소드를 자동으로 생성할 수 있습니다.
이 메소드들을 통해 id 와 name 필드 값에 접근 할 수 있게 되었습니다.
회원 리포지토리 생성
다음은 Member 도메인 객체에 접근하여 값을 저장, 수정, 불러오는 리포지토리 객체를 생성해야 합니다.
하지만 아직 데이터 저장소가 정의되지 않았음으로, 인터페이스를 먼저 작성한 후 구현체를 만들어 후에 상황에 맞게 변경하기 용이하게 만드는 것이 좋습니다. 때문에 인터페이스를 먼저 작성해야 합니다.
인터페이스란 ?
인터페이스란 자바의 다형성을 만족시키기 위해 나온 개념으로, JAVA8 이후로 나왔습니다.
interface 키워드로 선언하며, implements 키워드로 일반 클래스에서 구현합니다.
인터페이스에서는 절대 메소드를 구현해서는 안되며, 선언만 해야합니다.
또한 인터페이스를 구현하는 클래스는, 인터페이스에서 선언한 메소드를 하나도 빠짐 없이 모두 상황에 맞게 구현해야합니다.
매우 중요한 개념이고, 많은 블로그에서 다루는 내용이므로 추가로 찾아보는 것을 추천드립니다.
위 사진과 같이 repsitory 패키지를 먼저 만들었습니다. 이처럼 다른 기능을 하는 코드들은 패키지로 구분해 개별적으로 보관하는 것이 가독성도 좋고 효율적이라 하였습니다. MemberRepository 인터페이스를 선언한 후, 인터페이스를 구현하는 MemoryMemberRepository 클래스를 생성하였습니다.
우선 먼저 MemberRepository 인터페이스를 알아보겠습니다.
package com.example.demo.repository;
import com.example.demo.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
Member save(Member member);
Optional<Member> findById(Long Id);
Optional<Member> findByName(String name);
List<Member> findAll();
}
MemberRepository 인터페이스는 다음과 같은 4가지 기능을 합니다.
- 멤버를 등록하는 save 메소드. 저장한 멤버를 반환합니다.
- Id 값을 매개변수로 받는 findById 메소드. 해당하는 Id 에 맞는 멤버를 반환합니다. Optional 클래스로 반환하는 이유는 Null 값이 리턴될 수 있기 때문입니다. 자바에서는 반환 값으로 Null 값에 대비하기 위해 Optional 클래스를 자주 사용합니다.
- name 값을 매개변수로 받는 findByName 메소드. 해당하는 name 에 맞는 멤버를 반환합니다. 역시 Null 값이 반환될 수 있기 때문에 Optional 클래스를 사용하였습니다.
- 모든 멤버를 리스트 형식으로 출력하는 findAll 메소드.
다음으로는 이 MemberRepository 메소드들을 입맛에 맞게 구현한 MemoryMemberRepository 코드입니다.
package com.example.demo.repository;
import com.example.demo.domain.Member;
import java.util.*;
public class MemoryMemberRepository implements MemberRepository{ //멤버레포지토리를 구현하기로 한 클래스는 반드시 모든 메소드를 구현해야한다.
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override //인터페이스 오버라이드 구현
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long Id) {
return Optional.ofNullable(store.get(Id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore() {
store.clear();
}
}
우선 임의로 멤버를 저장할 자료구조가 필요했고, key, value 로 매칭시켜 데이터를 저장하는 Map 자료구조가 적합하다고 생각했기에 사용하였습니다. key 값으로 고유 id 값을 집어 넣었으며, value 값으로 멤버 객체가 들어갈 수 있게 선언하였습니다. 또한 고유 id 값인 sequence 을 0으로 초기화 시켰습니다.
save
인터페이스의 save 함수를 오버라이드 하여 구현하였습니다. 다음 id 값으로 넘어가야 하기 때문에 sequence 값을 1 증가시켜 멤버의 id 값으로 설정한 후, Map 내장 함수인 put 을 통해 멤버의 sequence 와 객체를 저장합니다. 메소드의 반환 값이 Member 객체이기 때문에 member 를 반환해줍니다.
findById
인터페이스의 findById 함수를 오버라이드 하여 구현하였습니다. 매개변수로 입력받은 id 값에 해당하는 멤버 객체가 없을 수 있기 때문에 Optional.ofNullable 메소드를 사용합니다. Null 이 아니라면 Map 자료구조의 내장함수인 get 함수를 이용해 key 값인 id 를 통해 멤버 객체를 반환합니다.
findByName
인터페이스의 findByName 함수를 오버라이드 하여 구현하였습니다. 기능은 findById 메소드와 같지만, store 의 key 값이 name 필드가 아닌 id 필드 이므로 위 메소드와 같이 간단히 작성할 수는 없습니다. 강의에서는 람다함수를 사용해 보다 직관적으로 구현하였습니다. 우선 store.values() 메소드를 통해 모든 value 값들인 멤버들을 불러온 후, stream() 메소드를 통해 불러온 멤버들을 람다식으로 객체 하나씩 접근해 아래 메소드를 실행합니다. 접근한 하나의 멤버 객체를 member 로 선언한 후, 이 객체의 name 필드 값과 매개변수로 받은 name 값이 같다면 findAny() 메소드로 해당 멤버 객체를 반환합니다.
findAll
인터페이스의 findAll 함수를 오버라이드 하여 구현하였습니다. store 의 모든 value 값들인 멤버들을 불러온 후 이를 ArrayList 자료구조에 집어넣은 후 이를 반환합니다. 한 번 선언하면 크기가 변하지 않는 List 와는 달리 ArrayList 는 크기가 가변적이며, index 로 쉽게 요소에 접근할 수 있기 때문에 장점이 많습니다.
이렇게 간단하지만 요구를 만족하는 회원 도메인과, 도메인에 접근해 저장하고 불러오고 수정하는 리포지토리를 구현해보았습니다. 다음 포스팅에서는 구현한 리포지토리 메소드들이 정상적으로 동작하는지에 대한 테스트 케이스를 만들어 테스트 하는 방법에 대해 알아보겠습니다.
'개인 공부 > Spring' 카테고리의 다른 글
[Spring] #6 스프링 빈 등록 (0) | 2022.01.19 |
---|---|
[Spring] #5 회원 서비스 구현 (0) | 2022.01.19 |
[Spring] #4 회원 리포지토리 테스트 케이스 작성 (0) | 2022.01.14 |
[Spring] #2 예제로 통한 백엔드 개발 (0) | 2022.01.12 |
[Spring] #1 스프링 웹 개발 기초 (0) | 2022.01.11 |