티스토리 뷰
도메인(Domain)이란 비즈니스 로직을 포함하는 객체(Object)를 의미한다. 회원, 주문, 상품 등 DB에 저장되고 관리되는 데이터를 의미하면 편하다.

스프링 생태계에서 도메인 객체는 컨트롤러, 서비스, 리포지토리 세 계층에서 사용된다.
리포지토리(Repository)는 DB에 직접 접근하여, 도메인 객체에 담긴 정보를 DB에 저장하고 불러오는 계층이다.
서비스(Service)는 애플리케이션의 핵심 비즈니스 로직들을 구현하는 계층이다.
컨트롤러(Controller)는 웹 MVC에서 컨트롤러에 해당되는 계층으로, 쉽게 말해 외부 요청을 특정 서비스로 연결한다.
가장 기본적인 회원(Member) 도메인을 사용한 비즈니스 로직을 생각해보자. 우리는 아래와 같은 간단한 비즈니스 로직을 생각해볼 수 있을 것이다.
- 회원 가입
- 회원 조회
도메인 객체
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(문자열) 필드가 있다.
getter와 setter는 자바빈(JavaBean) 규약에 정의된 메서드로, 필드를 private로 선언하여 직접적인 접근을 불가능하게 하고, 값에 접근하는 getter와 값을 수정하는 setter 두가지가 있다.
인텔리제이에서는 Generate(alt+insert) → getter and setter로 한번에 만들 수 있다.
리포지터리
import com.bdid.bdid.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();
}
실무에선 DB를 변경하는 경우가 발생할 수 있으므로 인터페이스로 구현한다.
회원을 가입하는 save, 요청값을 바탕으로 해당 회원정보를 찾아내는 findBy~, 저장된 회원 리스트를 반환하는 findAll 와 같은 메서드를 선언한다.
Optional은 java8부터 제공되는 객체로 null 값이 반환될 수 있는 경우를 쉽게 처리할 수 있도록 도와준다.
리포지터리 구현체
import com.bdid.bdid.domain.Member;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.stereotype.Repository;
@Repository
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());
}
public void clearStore(){
store.clear();
}
}
데이터 저장소는 아직 선정되지 않았으므로, 메모리 내에 Map 자료구조에 저장하는 MemoryMemberRepository 클래스를 구현한다.
@Repository는 추후에 설명하겠지만, 이 리포지터리가 스프링에서 관리하도록 하는 어노테이션이다.
서비스
import com.bdid.bdid.repository.MemberRepository;
import com.bdid.bdid.domain.Member;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
/**
* 회원가입
*/
public Long join(Member member){
//같은 이름이 있는 중복 회원X
validateDupicateMember(member);
memberRepository.save(member);
return member.getId();
}
/**
* 전체 회원 조회
*/
public List<Member> findMember(){
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId){
return memberRepository.findById(memberId);
}
private void validateDupicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
}
MemberService에서는 회원가입 로직을 처리하는 join과 전체 회원을 조회하는 findMember, id를 통해 회원을 조회하는 findOne 메서드가 존재한다.
회원가입시 중복된 이름이 가입되는걸 방지하기 위해 validateDuplicateMember 메서드를 만들었다. join에만 쓰이는 메서드지만 추후에 확장될 때 자주 사용될 수 있기 때문에 메서드로 따로 뺐다. 인텔리제이에서는 Extract Method(ctrl+alt+M)으로 가능하다.
Optional 객체는 람다식을 사용한 메서드 체이닝이 가능하다. ifPresent는 만약 Optional 객체가 null이 아닐 때 실행되는 메서드이다.
@Service는 아까 말했던 @Repository 어노테이션과 마찬가지로 스프링이 관리하도록 하는 어노테이션이다.
@Autowired는 의존성 주입에 관련된 것으로 추후에 설명하도록 하겠다.
컨트롤러
import com.bdid.bdid.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
컨트롤러는 아직 정확한 view가 나오지 않았으므로 의존성 주입만 해둔다.
@Controller는 스프링이 관리하도록 하는 어노테이션이다.
의존성 주입
public class MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
}
스프링은 의존성을 주입하는 방식을 사용한다. 위와 같이 개발자가 직접적으로 특정 객체를 연동하는 코드를 작성하는 것이 아니라 스프링에서 관리하는 객체(Bean)을 자동적으로 주입하여 연동한다.
@Controller
public class MemberController {
// 생성자 주입
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
// setter 주입
private MemberService memberService;
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService;
}
// 필드 주입
@Autowired private MemberService memberService;
}
기본적으로 의존성 주입에는 세가지 방식이 있는데, 스프링은 현재 생성자 주입을 가장 권장하고 있다.
의존성 주입이 되려면, 스프링 빈으로 등록이 되어있어야 한다. 일반적으로는 @Component를 붙여서 자동 등록하는데, @Controller나 @Service, @Repository는 내부에 @Component를 포함하고 있다. 이를 컴포넌트 스캔 방식이라한다.
@Autowired를 생성자에 사용할 때, 생성자가 1개라면 생략할 수 있다.
참고로 스프링은 스프링 컨테이너에 빈을 등록할때 싱글톤으로 등록한다. 설정에서 바꿀수 있긴하다.
import com.bdid.bdid.repository.MemberRepository;
import com.bdid.bdid.repository.MemoryMemberRepository;
import com.bdid.bdid.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}
@Autowired나 @Component를 사용하지 않고, Config파일을 만들어서 @Bean 어노테이션을 이용, 코드를 통해 빈을 등록하는 것도 가능하다. Config에서 일괄적인 수정이 가능해서 이것이 더 편한 경우도 있다.
그리고, 컴포넌트 스캔의 범위는 실행파일(프로젝트명Application)의 package와 그 하위 패키지이다.
- Total
- Today
- Yesterday
- oracle
- 필드
- SQL
- 레코드
- 테이블
- BufferedWriter
- Scanner
- db오브젝트
- 데이터베이스
- DB
- 배열
- db의 역사
- Java
- DBMS
- 입출력
- 알고리즘
- 자료구조
- SQL이란
- APS
- BufferedReader
- dialect
- StringBuilder
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |