백앤드/SpringBoot + Java 간간한 백앤드 예제

[SpringBoot + Java] 간단한 백엔드(domain, repository, service)

맏리믓 2023. 7. 16. 18:03

들어가며

- 백엔드의 주 목적은 서버간의 데이터 통신이다.

- 오늘은 그중 가장 기초적인 데이터 저장과 반환을 위한 domain, repository 를 만들어 볼 것이다.


일반적인 계층 구조

- 보통은 "controller, service, repository, domain, DB" 의 구조를 띈다.

  . controller : 웹 MVC 을 컨트롤 하는 역할

  . service : 핵심 비지니스 로직을 구현

  . repository : DB 에 접근, domain 객체를 DB 에 저장

  . domain : 비지니스의 데이터 객체


domain 생성

- 간단하게 만들 예정이라 id 와 name 으로 data class 를 구성 하였다.

- 우선 controller 생성과 비슷하게 "domain" 이란 이름의 디렉토리를 생성 해 준다.

 

- 그 후 Member class 를 생성 해 주고 코드를 작성 해 준다.

//Member.java
package com.example.learn_ex_member.domain;

public class Member {
    private Long id;
    private String name;

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }
}

 


Repository 생성

- 데이터를 관리 하기 위한 repository 를 생성 해 준다.

- 마찬가지로 repository 이름의 디렉토리를 생성 해 주고 우선은 interface 객체를 생성해 준다.

- 여기에는 함수의 이름, 파라미터 등 구현을 하지 않을 채로 존재 한다.

//MemberRepository.java
package com.example.learn_ex_member.repository;

import com.example.learn_ex_member.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();
}

 

- 그 후에는 실제 구현을 위해 실제 구현 코드를 작성 해 준다.

- 코드의 설명은 코드 옆에 적어 두었음

//MemoryMemberRepository.java
package com.example.learn_ex_member.repository;

import com.example.learn_ex_member.domain.Member;

import java.util.*;

public class MemoryMemberRepository implements MemberRepository {

    private static Map<Long, Member> store = new HashMap<>(); // 실무에서는 동시성 문제때문에 hashmap 을 사용하지는 않음
    private static long sequence = 0L;

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

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.ofNullable(store.get(id)); // null 일 가능성이 있기 때문
    }

    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream()
                .filter(member -> member.getName().equals(name)) // param 으로 들어온 name 과 같은 것을 찾음
                .findAny(); // 뭐라도 찾으면 아무것도 없으면?? Optional 에 null 로 반환
    }

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

Service

- service 에는 실제 비지니스에 관련된 기능들이 들어간다

- 예로 지금 진행 중인 프로젝트에서는 회원 가입 등을 들 수 있을 것이다.

 

- 동일하게 디렉토리와 class 를 생성 해 준다.

 

- 간단한 기능으로 회원 가입, 회원 찾기, 전체 회원 조회 등의 기능을 만들어 주었다.

 . 여기서 주의 해야 할 부분은 회원 가입에서 이미 존재하는 회원을 가입 시키려고 할 때이다.

 . 중복 되는 회원을 거르기 위해서 입력 된 이름으로 find 를 해 주고(findByName) 데이터가 존재 시 throw 를 해 주면 된다.

public void validateDuplicateMember(Member member){
    memberRepository.findByName(member.getName())
            .ifPresent((m -> {
                throw new IllegalStateException("존재하는 회원");
            }));
}

 

- 여기서 Test 코드를 작성 하게 되면 test 실행 시 MemberRepository 객체를 새로히 만들어서 사용하게 된다.

- 그렇게 되면 MemberService 에서의 repository 와 Test 에서의 repository 는 다른 객체로 실행 하는 꼴이 되어 버린다.

- 최대한 test 와 실제 실행을 맞추어 주기 위해 다음 작업을 해 준다.

 . MemberService 객체를 생성 할 때 외부에서 memberRepository 를 parameter 로 받아 memberRepository 에 넣어 주면 된다.

 . 코드로 보면 다음과 같다.

 public MemberService(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
 }

 

- 전체 코드

//MemberService.java
package com.example.learn_ex_member.service;

import com.example.learn_ex_member.domain.Member;
import com.example.learn_ex_member.repository.MemberRepository;
import com.example.learn_ex_member.repository.MemoryMemberRepository;

import java.util.List;
import java.util.Optional;

public class MemberService {
    private final MemberRepository memberRepository;

    //외부에서 넣어줌
    public MemberService(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }

    // 회원가입
    public Long join(Member member){
        // 중복 회원을 걸러 주기 위해
        validateDuplicateMember(member);
        memberRepository.save(member);
        return member.getId();
    }
    public void validateDuplicateMember(Member member){
        memberRepository.findByName(member.getName())
                .ifPresent((m -> {
                    throw new IllegalStateException("존재하는 회원");
                }));
    }

    //전체 회원 조회
    public List<Member> findMembers(){
        return memberRepository.findAll();
    }

    //id 를 통해 회원 조회
    public Optional<Member> findOne(Long memberId){
        return memberRepository.findById(memberId);
    }
}

마무리

- 여기까지 하면 구현은 끝이 난다.

- 재대로 구현 했는지 확인 하기 위한 test code 는 다음 포스팅에서 설명 하도록 하겠다.