[Java Spring 무작정 따라하기] 회원 도메인과 리포지토리 만들기

khj·2024년 7월 26일
post-thumbnail

💡 웹 애플리케이션의 구조

일반적인 웹 애플리케이션의 계층 구조는 다음과 같다.

컨트롤러는 웹 MVC의 컨트롤러 역할, 서비스는 비즈니스 로직, 도메인은 데이터베이스에 저장하고 관리되는 객체, 리포지토리는 데이터베이스에 접근, 도메인 객체를 저장, 관리 하는 역할을 한다.

강의에서는 아직 데이터베이스가 선정되지 않았다는 설정을 잡고 우선 인터페이스로 구현 클래스를 변경할 수 있도록 설계한다.

✨ 도메인 생성

우선 도메인으로 사용할 간단한 Member Class를 작성했다.

// hello.hello_spring/domain/Member.java
package hello.hello_spring.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;
    }
}

간단하게 각 데이터를 식별할 정수타입의 id와 문자열타입의 name만을 사용했다.
IntelliJ에서 Window를 기준으로 Alt + Insert 를 누르면 Getter와 Setter를 자동으로 생성해주는 Generate 기능이 호출된다.

✨ 리포지토리 생성

다음은 리포지토리 인터페이스를 작성했다.

// hello.hello_spring/repository/MemberRepository
package hello.hello_spring.repository;

import hello.hello_spring.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();
}

데이터를 저장하는 기능, 저장소에서 id 혹은 name으로 데이터를 검색하는 기능, 저장소의 모든 데이터를 반환하는 기능까지 총 4가지 기능을 만들 것이다.
(Optional 클래스를 사용하면 null 값을 명시적으로 처리할 수 있게 되고 NullPointerException을 방지할 수 있다고 한다.)

✨ 기능 구현

인터페이스를 만들었으니 구현체도 작성했다.
전체 코드는 다음과 같다.

// repository/MemoryMemberRepository.java
package hello.hello_spring.repository;

import hello.hello_spring.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());
    }
}

Member 객체를 저장하기 위한 Map을 선언 및 초기화했다.
Map<Long, Member> 부분에서 Long은 키의 타입이고, Member는 값의 타입이다.
HashMap은 키-값 쌍을 저장하고 검색하는 데 사용되는 데이터 구조라고 한다.

그리고 키 값을 생성하기 위한 정수타입의 sequence를 선언했다.

다음 코드는 객체를 저장하는 기능인 save다.

@Override
public Member save(Member member) {
    member.setId(++sequence);
    store.put(member.getId(), member);
    return member;
}

먼저 setId 메서드를 통해 sequence값을 하나 증가시켜 id값을 set 해주고 HashMap으로 선언했던 store에 member의 id와 member를 저장하고 member를 return 해준다.
그럼 member 객체가 생성되어 저장소에 저장되고 결과값으로 member 객체를 반환해 줄 것이다.

@Override
public Optional<Member> findById(Long id) {
    return Optional.ofNullable(store.get(id));
}

findById 기능은 store에서 데이터를 꺼내 반환해주면 된다.
store.get(id)를 통해 store에서 id값으로 구분해 원하는 특정 데이터를 가져오고 Optional.ofNullable을 통해 Null값이 반환될 때 발생할 수 있는 NullPointerException을 방지한다.

@Override
public Optional<Member> findByName(String name) {
    return store.values().stream()
        		.filter(member -> member.getName().equals(name))
        		.findAny();
}

findByName 기능은 findById와 마찬가지로 store에서 데이터를 꺼내 반환해주면 되지만 id값이 아닌 name값으로 데이터를 선택한다.
store.values()를 통해 모든 데이터를 가져오고 stream으로 변환된다.
가져온 값들은 filter의 lambda구문으로 member의 name이 찾으려는 name값과 동일한지 비교한다.
findAny()를 통해 만약 필터 조건에 맞는 요소가 하나라도 있다면 바로 반환이 된다.
만약 필터 조건에 맞지 않아도 Optional에 Null이 포함되어 반환된다.

@Override
public List<Member> findAll() {
    return new ArrayList<>(store.values());
}

마지막으로 findAll은 데이터에 있는 모든 값을 ArrayList에 담아 반환하는 기능이다.

만들어진 기능들은 TestCase를 작성해 확인할 예정이다.

profile
Spring, Django 개발 블로그

0개의 댓글