[SpringBoot + Java] 간단한 백엔드(domain, repository, service)
들어가며
- 백엔드의 주 목적은 서버간의 데이터 통신이다.
- 오늘은 그중 가장 기초적인 데이터 저장과 반환을 위한 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 는 다음 포스팅에서 설명 하도록 하겠다.