[Spring]<입문> 3편

BeenLab·2023년 7월 28일

스프링 입문

목록 보기
3/4

0. 들어가며

이번 글에서는 회원(도메인) 객체 생성, 회원을 저장할 저장소 인터페이스, 마지막으로 저장소 인터페이스를 메모리 형태로 구현하는 과정을 복기 해봅니다.

도메인: 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됩니다.

1. 회원 객체

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; //인스턴스 변수에 setName 의 인자로 넘어온 값 할당
 }
}

getter 와 setter 가 있는 간단한 객체 입니다.

2. 회원 저장소 인터페이스

package hello.hellospring.repository;
import hello.hellospring.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();
}

인터페이스는 다른 클래스 작성시 필요한 틀이라고 이해하면 되겠습니다.
Optional 은 Null 이 올 수 있는 곳을 감싸는 Wrapper 클래스 입니다.
NullPointerException을 방지할 수 있죠.

3. 저장소 > 메모리를 통한 구현

DB가 정해지지 않았기에 메모리로 구현합니다.
동시성 문제가 고려되어 있지 않습니다. 실무에서는 ConcurrentHashMap, AtomicLong 사용을 고려 해볼 수 있습니다.

package hello.hellospring.repository;
import hello.hellospring.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 를 통해 메서드 내용을 작성합니다.
save 메서드는 생성된 회원 인스턴스를 인자로 받습니다.
회원의 인스턴스의 Id 값은 save 매서드에서 정하고 있네요.

@Override
public class MemoryMemberRepository implements MemberRepository {
 private static Map<Long, Member> store = new HashMap<>();
 private static long sequence = 0L;
 
 public Optional<Member> findById(Long id) {
 return Optional.ofNullable(store.get(id));
 }
}

id 값에 맞는 회원을 찾습니다. 저장소에 있지 않은 id 값을 인자로 받으면 NullpointerException이 발생할텐데요,Optianal.ofNullabe 로 감싸서 return 하면 클라이언트 측에서 대응 할 수 있습니다.

@Override
public class MemoryMemberRepository implements MemberRepository {
 private static Map<Long, Member> store = new HashMap<>();
 private static long sequence = 0L;
 
 public List<Member> findAll() {
 return new ArrayList<>(store.values());
 }
}

findAll 메서드는 회원 객체를 요소로 가지는 리스트를 반환 합니다.
ArrayList 의 생성자의 인자가 store.values() 인데,
store.values 는 map의 value 값들을 Collection 형태로 반환 합니다.

 @Override
 public class MemoryMemberRepository implements MemberRepository {
 private static Map<Long, Member> store = new HashMap<>();
 private static long sequence = 0L;
 
 public Optional<Member> findByName(String name) {
 return store.values().stream()
 .filter(member -> member.getName().equals(name))
 .findAny();
 }
}

findByName 에서는 람다식을 사용하고 있습니다. 아직은 친한 문법이 아니라서 적응하려면 시간이 필요할거 같네요.
위에서 언급 했지만 stroe.values() 를 통해 컬렉션 타입을 얻고, 컬렉션 타입에서 .stream() 메서드를 통하여 스트림을 생성 할 수 있습니다.
member -> : 메서드의 인자라고 보면 됩니다.
람다식 인자로 받은 회원 객체의 이름과 findByName 의 인자 값과 비교하여 같은 것만 스트림에 남깁니다. 이후 findAny() 를 통해 하나의 요소만 Optional 형태로 반환 합니다.
추가로 findAny() 와 findFirst() 의 차이는 Stream 을 병렬로 다룰 때 findFirst() 는 항상 같은 값을 반환 하지만 findAny()는 요소가 여러개면 다른것을 반환 할때도 있습니다.

profile
코더 그 이상

0개의 댓글