본문 바로가기
  • 개발공부 및 일상적인 내용을 작성하는 블로그 입니다.
Spring basic

스프링 입문 : 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 비즈니스 요구사항 정리 및 회원 도메인과 레포지토리 만들기

by 방구석 대학생 2021. 11. 3.

"인프런의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의를 듣고 작성한 글 입니다."

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8

 

[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., 스프링 학습 첫 길잡이! 개발 공부의 길을 잃지 않도록 도와드립니다. 📣 확인해주세

www.inflearn.com

 

회원관리 예제 - 비즈니스 요구사항 정리

회원 ID 와 이름을 데이터로 가지는 회원 객체를 기본으로 하며, 회원 등록, 조회 등의 기능을 구현해본다.

데이터 저장소는 아직 선정되지 않았다는 가상의 시나리오를 적용한다.(아직 DB 가 선정되지 않은 상태)

이와 같은 예제를 통해 스프링 생태계가 전반적으로 어떤 식으로 개발이 이루어지고 동작하는지를 알아보자.

 

일반적인 웹 어플리케이션은 다음과 같은 계층 구조를 이루고 있다.

 

컨트롤러 : 웹 MVC 의 컨트롤러 역할을 담당한다.

서비스 : 핵심적인 비즈니스 로직을 구현하는 곳, 비즈니스 도메인 객체를 가지고 핵심 비즈니스 로직이 동작하도록 구현한 계층이다.

(진행했던 pro.gg 프로젝트에서는 비즈니스 로직을 서비스 계층이 아닌 컨트롤러 계층에서 구현했는데, 이는 아무래도 잘못된 설계였던것 같다....)

레포지토리 : 데이터베이스에 접근하는 계층이며 도메인 객체를 DB 에 저장하고 관리한다.

도메인 : 비즈니스 도메인 객체이다.

예시) 회원, 주문, 쿠폰 등등 과 같은 데이터들은 데이터베이스에 저장하고 관리된다. 즉, 데이터베이스에 저장되고 관리되는 데이터들을 비즈니스 도메인 객체라고 말한다.

 

* 이번 예제에서의 클래스간 의존관계

 

MemberService : 회원 비즈니스 로직을 담당한다.

MemberRepository(interface) : 아직 데이터베이스가 선정되지 않은 상황을 가정하고 있기 때문에 구현체를 우선 Memory 구현체로 만든다.(Memory 모드로 단순하게 저장하는 방식, 즉 개발을 진행하기 위해 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소를 사용한다. - 향후에 RDB 든 JPA 든 데이터 저장방식이 확정되면 이걸 바꿔보도록 하자.)

- interface : 추후에 데이터베이스가 확정되었을 때 그걸 바꿔끼우기 위해 interface 로 MemberRepository 를 만들어 놓는다.

 

 

회원 도메인과 레포지토리 만들기

회원 도메인 객체 클래스를 만든다.

- Member.java

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;
    }
}

회원 레포지토리 인터페이스를 만든다.

- MemberRepository.java

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);
    // Optional 이란? : java8 에서부터 추가된 기능, 간단하게 말하자면 findById 와 같은 메소드를 통해 반환값을 가져오는데, 결과가 null 일 경우가 있을수 있다. 요즘은 null 값이 돌아온다고
    // 그를 그대로 받기보다는 Optional 로 감싸서 반환해주는 것을 선호 한다고 한다.
    List<Member> findAll();
}

 

위의 MemberRepository 소스 코드를 보면 findById, findByName 메소드를 Optional<T> 형태로 반환타입을 선언해 준것을 볼 수 있는다.

여기서 Optional<T> 란 java8 에서부터 추가된 기능으로서, findById 와 같은 메소드를 통해 반환값을 가져올 때 결과값이 null 인 경우가 있을 경우 if 와 같은 조건문을 통해 null 값을 처리하는 것이 아니라 Optional<T> 의 형태로 감싸서 반환받은후, Optional<T> 클래스 타입에서 제공해주는 메소드들을 통해 간편하게 null 값 반환에 대한 처리를 도와주는 기능을 수행한다. 

 

회원 레포지토리 메모리 구현체를 만든다.

아래의 소스 코드를 통해 Optional<T> 형태로 값을 반환받을 때 null 값에 대한 처리를 메소드로 어떻게 간편하게 수행 하는지를 확인할 수 있다.

- MemoryMemberRepository.java

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; // 0, 1, 2 와 같이 순차적으로 데이터에 대한 키값을 생성해주는 필드(기본키 sequence 전략을 말하는 것 같음)

    @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)); // null 값이 반환될경우 Optional 로 감싸줄 수 있다.
    }

    @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());
    }
}