[Spring] Service, DAO 그리고 VO

2023. 11. 7. 20:56학부 강의/웹프로그래밍 (Spring)

0. 출처

 

아직 배우고 있는 중이라 부정확한 정보가 포함되어 있을 수 있습니다!
주의하세요!

 

 

올인원 스프링 프레임워크 참고.

 

올인원 스프링 프레임워크 : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

 


 

1. 예제

 

가. 환경 설정

 

서버에 올라가 있는 ch07_pjt_01을 내린다.

 

ch07_pjt_01을 복사해서 ch08_pjt_01 프로젝트를 생성한다.

 

pom.xml을 수정한다. (ch07_pjt_01ch08_pjt_01)

 


 

2. 데이터 전송

 

sign_up.jsp다.

 

회원가입을 원할 때 사용된다.

<!-- sign_up.jsp -->

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>This page is SIGN_UP</h3>

    <form action="/hello/signUpConfirm">
        ID : <input type="text" name="m_id"><br/>
        PW : <input type="password" name="m_pw"><br/>
        MAIL : <input type="text" name="m_mail"><br/>
        PHONE : <input type="text" name="m_phone"><br/>
        <input type="submit" value="SIGN UP">
        <input type="reset" value="CANCEL">
    </form>

</body>
</html>
  • <form action="/hello/signUpConfirm"> : SIGN UP을 누르면 입력된 회원 정보가 서버로 전송된다. 이때 정보를 받아서 처리하는 url은 /hello/signUpConfirm이다.

 


 

가. RequestMapping

//MemberController.java

@Controller
public class MemberController {

    ...

    @RequestMapping("/signUpConfirm")
    public String signUpConfirm(@RequestParam String m_id, @RequestParam int m_pw, @RequestParam String m_mail, @RequestParam String m_phone) {
        System.out.println("[MemberController] signUpConfirm()");

        System.out.println("m_id: " + m_id);
        System.out.println("m_id: " + m_pw);
        System.out.println("m_id: " + m_mail);
        System.out.println("m_id: " + m_phone);

        return null;
    }

    ...

/signUpConfirm에 RequestMapping된 메서드다.

 

@RequestMapping("/signUpConfirm")
public String signUpConfirm(@RequestParam String m_id, 
                                                        @RequestParam int m_pw, 
                                                        @RequestParam String m_mail, 
                                                        @RequestParam String m_phone){ ... }
  • @RequestParam String m_id : 입력 데이터 중 name이 m_id인 값을 전달받는다.
  • 이하 동일.

 

 

form으로 입력된 정보를 서버에서 확인할 수 있다.

 


 

나. VO 객체

 

@RequestParam을 이용해서 데이터를 받을 수 있지만 전달받는 데이터가 많아지면 @RequestParam를 반복해야한다.

 

너무 길고 복잡해진다.

 

이런 경우 VO 객체를 이용한다.

 

 

Value object - Wikipedia

From Wikipedia, the free encyclopedia In computer science, a value object is a small object that represents a simple entity whose equality is not based on identity: i.e. two value objects are equal when they have the same value, not necessarily being the s

en.wikipedia.org

 

  • VO
    : Value Object
    : 값 그 자체를 나타내는 객체다.
    : Entity가 아닌 value로 equality를 판단. (필드의 값만 같다면 같은 객체로 인식. Just like 화폐)
    : Value objects should be immutable. 데이터를 수정할 수 없다. 생성부터 폐기까지 불변하다.

 

package com.company.hello.member;

public class MemberVo {

    String m_id;
    String m_pw;
    String m_mail;
    String m_phone;


    public String getM_id() {
        return m_id;
    }

    public void setM_id(String m_id) {
        this.m_id = m_id;
    }

    public String getM_pw() {
        return m_pw;
    }

    public void setM_pw(String m_pw) {
        this.m_pw = m_pw;
    }

    public String getM_mail() {
        return m_mail;
    }

    public void setM_mail(String m_mail) {
        this.m_mail = m_mail;
    }

    public String getM_phone() {
        return m_phone;
    }

    public void setM_phone(String m_phone) {
        this.m_phone = m_phone;
    }

}

form의 input tag의 name과 동일한 이름의 필드를 포함한다.

 

Getter, Setter 메서드를 선언한다. (필수!)

 

package com.company.hello.member;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class MemberController {

    @RequestMapping("/signUp")
    public String signUp() {
        return "sign_up";    
    }

    @RequestMapping("/signIn")
    public String signIn() {
        return "sign_in";
    }

    /*
    @RequestMapping("/signUpConfirm")
    public String signUpConfirm(@RequestParam String m_id, @RequestParam int m_pw, @RequestParam String m_mail, @RequestParam String m_phone) {
        System.out.println("[MemberController] signUpConfirm()");

        System.out.println("m_id: " + m_id);
        System.out.println("m_id: " + m_pw);
        System.out.println("m_id: " + m_mail);
        System.out.println("m_id: " + m_phone);

        return null;
    }
    */

    @RequestMapping("/signUpConfirm")
    public String signUpConfirm(MemberVo memberVo) {
        System.out.println("[MemberController] signUpConfirm()");

        System.out.println("m_id: " +memberVo.getM_id());
        System.out.println("m_id: " +memberVo.getM_pw());
        System.out.println("m_id: " +memberVo.getM_mail());
        System.out.println("m_id: " +memberVo.getM_phone());

        return null;
    }
}
  • public String signUpConfirm(MemberVo memberVo) { : Mapping된 메서드의 매개변수로 VO 객체를 선언한다. 필드의 이름이 전달받는 데이터와 같다면 자동으로 필드에 저장된다. 이때 Setter 메서드를 사용한다.
  • memberVo.getM_id() : VO 객체에서 데이터를 가져오는 방법.

 


 

보충 설명 : VO와 DTO

 

VO는 상황에 따라서 DTO와 구분하지 않고 사용하는 경우가 있다.

 

  • DTO
    : Data Transfer Object
    : 프로세스 간에 데이터를 전달하는 객체.

 

DTO는 데이터를 변환하고 전달하는 것이 주목적이다.

 

이에 Getter, Setter 외에 다른 메서드를 선언하지 않는다.

 

DTO는 Setter 메서드를 가지고 있어서 데이터가 수정될 수 있다.

 

반면 VO는 데이터의 값 그 자체가 중요하기 때문에 수정을 금지하고 있다.

 

그래서 Getter만 있고 Setter 메서드가 없다.

 

오직 생성자를 통해서만 필드를 초기화한다.

 

VO는 DTO가 아니지만 둘은 자바 개발자들에 의해서 혼용되기도 한다.

 

VO - 불변, 보존         DTO - 전달, 변환

 

그래서 위의 MemberVo는 Setter 메서드가 있으므로 굳이 따지자면 DTO에 가깝다.

 


 

3. Service

package com.company.hello.member;

import org.springframework.stereotype.Service;

@Service
public class MemberService {

    public int signUpConfirm(MemberVo memberVo) {
        System.out.println("[MemberService] signUpConfirm()");

        return 0;
    }
}

@Service 사용해서 서비스를 생성한다.

 

간단간단!

 

@Controller
public class MemberController {

    @Autowired
    MemberService memberService;

컨트롤러에서 자동으로 주입된다.

 

+어노테이션 정리

어노테이션 적용 위치 목적 특징
@Bean 메서드 스프링 컨테이너에 수동으로 빈을 등록하고자 할 때 사용 - 주로 @Configuration 어노테이션이 붙은 클래스에서 사용
@Component 클래스 임의의 클래스를 스프링 빈으로 자동 등록하고자 할 때 사용 - 가장 일반적인 형태의 스테레오타입 어노테이션입니다.
@Service 클래스 비즈니스 로직을 처리하는 서비스 레이어의 클래스를 스프링 빈으로 등록하고자 할 때 사용 - @Component의 구체화된 형태로, 비즈니스 로직 서비스를 명확히 표시하는 데 사용.
- 특별한 기능을 추가하지는 않지만, 코드의 의도를 명확히 할 수 있음.
@Repository 클래스 데이터 저장소의 CRUD 작업을 담당하는 클래스를 스프링 빈으로 등록하고자 할 때 사용 - @Component의 구체화된 형태로, 데이터 액세스 예외 변환 및 DAO 역할을 명확히 표시하는 데 사용.

 

 


 

4. DAO

 

아직 진짜 데이터베이스를 연결하진 않는다.

 

Map으로 데이터베이스를 대신한다.

package com.company.hello.member;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.springframework.stereotype.Component;

@Component
public class MemberDao {

    private Map<String, MemberVo> memberDB = new HashMap<String, MemberVo>();

    public void insertMember(MemberVo memberVo) {
        System.out.println("[MemberDao] insertMember()");

        System.out.println("m_id: " + memberVo.getM_id());
        System.out.println("m_pw: " + memberVo.getM_pw());
        System.out.println("m_mail: " + memberVo.getM_mail());
        System.out.println("m_phone: " + memberVo.getM_phone());

        memberDB.put(memberVo.getM_id(), memberVo);
        printAllMember();
    }

    private  void printAllMember() {
        System.out.println("[MemberDao] printAllMember()");

        Set<String> keys = memberDB.keySet();
        Iterator<String> iterator = keys.iterator();

        while(iterator.hasNext()) {
            String key = iterator.next();
            MemberVo memberVo = memberDB.get(key);

            System.out.println("m_id: " + memberVo.getM_id());
            System.out.println("m_pw: " + memberVo.getM_pw());
            System.out.println("m_mail: " + memberVo.getM_mail());
            System.out.println("m_phone: " + memberVo.getM_phone());
        }

    }
}
  • @Component : @Repository를 사용해도 상관없다.

 

package com.company.hello.member;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MemberService {

    @Autowired
    MemberDao memberDao;  // 추가

    public int signUpConfirm(MemberVo memberVo) {
        System.out.println("[MemberService] signUpConfirm()");

        memberDao.insertMember(memberVo);  // 추가

        return 0;
    }
}
  • @Autowired MemberDao memberDao : 의존 객체 memberDao를 자동으로 주입한다.
  • memberDao.insertMember(memberVo); : DAO 객체에 VO 객체를 전달한다.

 

 


 

5. View

 

 

Controller에서 View에 대한 정보를 반환하면 View Resolver가 적합한 View(JSP 파일)을 선택한다.

@Controller
public class MemberController {

    ...

    /*
    @RequestMapping("/signUpConfirm")
    public String signUpConfirm(@RequestParam String m_id, @RequestParam int m_pw, @RequestParam String m_mail, @RequestParam String m_phone) {

        ...

        return null;
    }
    */

    @Autowired
    MemberService memberService;

    @RequestMapping("/signUpConfirm")
    public String signUpConfirm(MemberVo memberVo) {
        ...
        memberService.signUpConfirm(memberVo);

        return "sign_up_ok"; // 수정
    }
}
  • return "sign_up_ok"; : Controller의 메서드는 사용할 View에 대한 정보를 반환한다.

 

<!-- servlet-context.xml -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
</beans:bean>

servlet-context.xml에서 InternalResourceViewResolver의 설정에 따라서 /WEB-INF/views/sign_up_ok.jsp을 찾는다.

 

<!-- sign_up_ok.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

This page is SIGN_UP_OK.

</body>
</html>

sign_up_ok.jsp다.

 

 

해당 화면이 나오면 관리자 계정 생성에 대한 요청이 정상적으로 처리된 것이다.

 


 

6. 로그인

<!-- sign_in.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>This page is SIGN_IN.</h3>

    <form action="/hello/signInConfirm">
        ID : <input type="text" name="m_id"><br/>
        PW : <input type="password" name="m_pw"><br/>
        <input type="submit" value="SIGN IN">
        <input type="reset" value="CANCEL">
    </form>
</body>
</html>

form에 로그인 정보를 입력하고 SIGN IN 버튼을 누른다.

 

//MemberController.java
...
@RequestMapping("/signInConfirm")
public String signInConfirm(MemberVo memberVo) {
    System.out.println("[MemberController] signUpConfirm()");

    MemberVo signInedMember = memberService.signInConfirm(memberVo);

    if(signInedMember != null) 
        return "sign_in_ok";   //로그인 성공
    else                
        return "sign_in_ng";   //로그인 실패
}
  • @RequestMapping("/signInConfirm")
    : MemberControllersignInConfirm에서 로그인 요청을 처리한다.
  • MemberVo signInedMember = memberService.signInConfirm(memberVo);
    : 로그인 정보를 memberVo로 전달받아서 이를 처리하는 서비스로 전달한다.
  • if(signInedMember != null) : 서비스와 DAO의 처리 결과에 따라서 사용할 View가 달라진다.

 

//MemberService.java
...
public MemberVo signInConfirm(MemberVo memberVo) {
    System.out.println("[MemberService] signInConfirm()");

    MemberVo signInedMember = memberDao.selectMember(memberVo);

    return signInedMember;
}

서비스에선 전달받은 memberVo를 다시 memberDao로 전달한다.

 

//MemberDao.java
...
public MemberVo selectMember(MemberVo memberVo) {
    System.out.println("[MemberDao] selectMember()");

    MemberVo signInedMember = memberDB.get(memberVo.getM_id());

    if(signInedMember != null && memberVo.getM_pw().equals(signInedMember.getM_pw()))
        return signInedMember;
    else
        return null;
}

MemberDao에선 전달받은 id의 password가 DB에 저장된 password와 같은지 확인한다.

 

올바르지 않은 접근이라면 null을 반환한다.

 

최종적으로 컨트롤러는 처리의 결과에 알맞은 View의 정보를 반환한다.

 

모델의 정보를 바탕으로 View를 완성해서 클라이언트에게 전달한다.

 

로그인 성공         로그인 실패

 


 

이미지 출처 :https://kr.freepik.com/free-photo/modern-technology-of-assembly-of-cars-the-plant-of-the-automotive-industry-shop-for-the-production-and-assembly-of-machines-top-view-the-process-of-welding-parts-of-the-car_26150948.htm#page=4&query=컨베이어 벨트&position=49&from_view=keyword&track=ais