Notice
Recent Posts
Recent Comments
Links
«   2025/04   »
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
Today
In Total
관리 메뉴

A Joyful AI Research Journey🌳😊

[63] 230329 Spring: 스프링을 이용한 MVC 구현 [K-디지털 트레이닝 63일] 본문

🌳Bootcamp Revision✨/Spring Framework, Java

[63] 230329 Spring: 스프링을 이용한 MVC 구현 [K-디지털 트레이닝 63일]

yjyuwisely 2023. 3. 29. 10:50

230329 Wed 63rd class
Ch. 18 MVC 패턴 구현
진도: Ch. 18 MVC 패턴 구현에 기반한 자체 수업 (교재: 최범균의 JSP 2.3 웹 프로그래밍: 기초부터 중급까지, 저자: 최범균)
책 예제 코드: https://github.com/madvirus/jsp23
저자 블로그: https://javacan.tistory.com/

 

최범균의 JSP 2.3 웹 프로그래밍: 기초부터 중급까지 | 최범균 - 교보문고

최범균의 JSP 2.3 웹 프로그래밍: 기초부터 중급까지 | [최범균의 JSP 2.3 웹 프로그래밍 기초부터 중급까지]는 JSP 2.3의 새로운 특징 반영과 JSP를 지원하는 요소인 서블릿과 표현 언어 등의 새로운

product.kyobobook.co.kr

참고 
https://wikidocs.net/book/5792

 

스프링 MVC 하루만에 배우기

이 시리즈는 책으로 출간된 [스프링 MVC 하루만에 배우기](http://www.yes24.com/Product/Goods/96581707) 를 다룹니다. ![스프링 MVC…

wikidocs.net

https://www.javatpoint.com/spring-tutorial

 

Learn Spring Tutorial - javatpoint

Learn Spring Tutorial. This spring tutorial for beginners and professionals provides in depth learning of DI, AOP, Data Access, MVC, Remoting, ORM and Integration.

www.javatpoint.com


오늘 배운 것 중 기억할 것을 정리했다.


다시 기억할 것 

클래스로부터 객체를 생성하려면 다음과 같이 new 연산자를 사용한다.

public class Car{ //Car클래스 선언
}
//Car클래스의 객체 생성
new Car();

new는 클래스로부터 객체를 생성시키는 연산자이다.
new 연산자는 힙 영역에 객체를 생성시킨 후 객체의 번지를 리턴하도록 되어 있다. 

//클래스로 선언된 변수에 new 연산자가 리턴한 객체의 번지를 저장한다.
클래스 변수;
변수 = new 클래스();
//1개의 실행문으로 작성할 수 있다.
클래스 변수 = new 클래스();

2023.02.18 - [💻 Self-Study Revision✨/나의 개발 일지✍🏻] - 🌷[K-디지털 트레이닝 24일차~] Java: 다시 기억할 것 목록


프로젝트의 구성


Business: 서비스 (ex. 회원가입 insert, 로그인 서비스 -> MyBatis -> DB) 

유지보수까지 고려한 구조이다.
단순하게 화면을 띄우는 것은 DB작업을 안 해도 된다.


http://localhost:8080/(웹브라우저) -> controller -> WEB-INF/views/home.jsp를 실행한다.
/: @RequestMapping의 value가 /인 것을 실행한다. 
메서드는 기본적으로 다 GET방식이다.
단순하게 화면을 연결한다. (-> Presentation 영역만 있으면 된다.)
DB 작업은 하지 않으므로, 서비스는 필요없다.
(-> DB, MyBatis는 사용하지 않는다.) 

코드) HomeController.java 일부

package org.hj.controller;
import java.util.Locale;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
return "home"; //home이라는 메서드를 실행한다. 메모장의 URL주소가 호출한 것이다.
// /WEB-INF/views/home.jsp
}

만약 main.jsp를 원하면 home -> main으로 하면 된다.


컨트롤러를 나눠야 다른 팀원들끼리 작업이 가능하다.

회원가입

http://localhost8080/member -> MemberController -> WEB-INF/views/member/memberin.jsp
회원가입 화면이 나온다.

코드) MemberController.java 일부

package org.hj.controller;
import javax.servlet.http.HttpSession;
import org.hj.model.LoginVO;
import org.hj.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MemberController {
@Autowired
LoginService ls;
// 회원가입 //화면 실행을 위한 멤버 (GET방식)
@RequestMapping(value = "/member", method = RequestMethod.GET) //member 메서드 GET 방식
public String member () {
return "member/memberin"; //멤버 폴더의 memberin.jsp를 실행한다.
}

만약 오류가 난다면 이클립스에서 서버 더블클릭 후 Modules에서 Path가 /로 되어있는지 체크해본다.

결과)


아이디 중복 체크 등도 자바스크립트로 한다.

코드) memberin.jsp 일부
(경로: SpringEx\src\main\webapp\WEB-INF\views\member\memberin.jsp)
기본 방식은 GET인데, 보안상 숨겨야하므로 POST방식을 선택한다.

<h1>회원가입</h1>
<form action="/member" method="post"><!-- 메서드방식:POST -->
아이디 <input type="text" name="id">
비밀번호 <input type="text" name="password">
별명 <input type="text" name="name">
<input type="submit" value="가입하기">
</form>

MemberController.java와 비교했을 때 메서드 방식이 POST이므로 아래 두번째의 회원가입 서버를 실행한다.

코드) MemberController.java 일부
실제로 insert를 하기 위해 실행된다. 

@Controller
public class MemberController {
@Autowired
LoginService ls; //LoginService ls = new LoginService(); 클래스를 new를 써서 객체화한 것
// 회원가입서버 //실제로 가입하기 위한 멤버 (POST방식)
@RequestMapping(value = "/member", method = RequestMethod.POST) //둘 다 GET이면 충돌이 일어난다. 메서드 방식이 POST이다.
// int a //member는 참조 변수
//public String memberPost(String id, String password, String name) //메모리 차지가 크므로 쓰지 않는 방법이다. private 같은 접근 제한자도 없다.
public String memberPost (LoginVO member) { // 모델: LoginVO
System.out.println(member); //member.toString()이 생략된 것이다.
//#####새로 추가함#####
ls.memreg(member);
return "board/list"; //board 폴더의 list.jsp를 실행한다. 게시판 목록을 보여준다.
}
}

LoginVO 모델을 쓰는 이유

예시) 자바의 메서드 선언과 메서드 호출
인수가 매개변수에 들어간다.

//메서드 선언
public String memberPost(int a){ //매개변수
return "board/list";
}
//메서드 호출
memberPost(10); //인수

스프링이 내부적으로 memberPost("id1234", "5678910", "유자바")를 처리한다.
자동으로 해준다. 그렇게 하도록 설계되어있다.

//메서드 선언
public String memberPost(String id, String password, String name){ //매개변수
return "board/list";
}
//메서드 호출 (스프링이 내부적으로 처리하는 것)
memberPost("aaa", "1234", "정자바");

메모리 차지가 크므로 쓰지 않는 방법이다. private 같은 접근 제한자도 없다. 
-> 효율적이지 않으므로 모델인 LoginVO을 쓴다. (모델은 필수가 아니고 선택이다.) 

모델을 쓰는 이유: private이 있고, String 참조 타입(주소를 저장)을 쓴다.
LoginVO (8바이트만 차지한다.)
-> 더 효율적이다.

코드) MemberController.java LoginVO.java

코드) LoginVO.java 일부

package org.hj.model;
public class LoginVO {
private String id;
private String password;
private String addr;
private String phone;
private String email;
private String name;
private int age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}

코드) MemberController.java 일부
실제로 insert를 하기 위해 실행된다. 

// 회원가입서버 //실제로 가입하기 위한 멤버 (POST방식)
@RequestMapping(value = "/member", method = RequestMethod.POST) //둘 다 GET이면 충돌이 일어난다. 메서드 방식이 POST이다.
// int a //member는 참조 변수
//public String memberPost(String id, String password, String name) //메모리 차지가 크므로 쓰지 않는 방법이다. private 같은 접근 제한자도 없다.
public String memberPost (LoginVO member) { // 모델: LoginVO
System.out.println(member); //member.toString()이 생략된 것이다.
//#####새로 추가함#####
ls.memreg(member);
return "board/list"; //board 폴더의 list.jsp를 실행한다. 게시판 목록을 보여준다.
}

LoginVO member (ex. int a)

int a = 10;
System.out.println(a); //출력할 때는 변수 이름 a를 쓴다.

결과)

콘솔창에 인출된 값이 나온다.

LoginVO [id=id0329, password=5678910, addr=null, phone=null, email=null, name=유자바, age=0]

컨트롤러와 서비스의 연결

코드) MemberController.java 일부

컨트롤러와 서비스를 연결하려면 서비스(LoginService)가 컨트롤러(MemberController.java)에 포함되어 있어야 한다. (포함 관계가 되어야한다.)

import org.hj.service.LoginService;
@Controller
public class MemberController {
@Autowired
LoginService ls; //LoginService ls = new LoginService(); 클래스를 new를 써서 객체화한 것.
//LoginService는 인터페이스이므로 원래 new는 안 되는 데 위에 @AutoWired 이용해서 가능하게 했다.
}

=> MemberController.java안에 LoginService가 포함되어 있다.


Presentation    Business    Persistencetier   DB
SpringMVC <---------------> Spring Core  <---------------> MyBatis <------------>  
  <-------------------------------------------------------------------------------------------------------------  
MemberController.
java
  LoginService.
java 
  LoginMapper
from
LoginServiceImpl.
java
   
메서드
member();
memberPost();
--------------->
member
데이터를 넘긴다.
ls

메서드

memreg();
login();
--------------->
member
데이터를 넘긴다.
lm

메서드

memreg();
login();
   

데이터를 비즈니스 영역에 준다. -> 그 후 DB까지 전달된다. 

메서드의 return에 의해 DB에서 화면으로 (호출한 곳으로) 되돌아간다.

Mapper는 구현 메서드가 존재하지 않는다. return을 사용하지 않는다.
코드) LoginMapper.xml 일부

<select id="login" resultType="org.hj.model.LoginVO"> <!-- 구현메서드가 없으니까 return이 없다. -->
<!-- 대신 select는 resultType이라는 타입이 return 역할이다. -->
select id, password
from member
where id=#{id} and password=#{password}
</select>

member 데이터는 아래와 같다.

LoginVO [id=id0329, password=5678910, addr=null, phone=null, email=null, name=유자바, age=0]

코드) LoginService.java
설계 역할이다.
추상메서드만 설계했다.

package org.hj.service;
import org.hj.model.LoginVO;
public interface LoginService { //인터페이스가 new()를 할 수 없으므로 MemberController.java에 @Autowired를 설정
//#####새로 추가함#####
public void memreg(LoginVO member); //추상메서드이므로 LoginServiceImpl.java에서 구현한다.
public LoginVO login(LoginVO member);
}

코드) LoginServiceImpl.java
implements를 사용해서 구현한다.

package org.hj.service;
import org.hj.mapper.LoginMapper;
import org.hj.model.LoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
LoginMapper lm;
//#####새로 추가함#####
public void memreg(LoginVO member) {
lm.memreg(member);
};
public LoginVO login (LoginVO member) {
System.out.println("service="+member);
System.out.println("service return="+lm.login(member));
return lm.login(member);
}
}

Mapper 인터페이스

코드) LoginMapper.java
Mapper 인터페이스는 Mapping 파일에 기재된 SQL을 호출하기 위한 인터페이스이다.
참고: https://bigstupid.tistory.com/23

package org.hj.mapper;
import org.hj.model.LoginVO;
public interface LoginMapper {
//#####새로 추가함#####
public void memreg(LoginVO member);
public LoginVO login(LoginVO member);
}

아래처럼 사용했을 때 불편한 점은 SQL문장이 길어지면 사용하기 불편하다는 것이다. 그래서 Mapper를 사용한다.

select *
from(select @rownum:=@rownum+1 rownum, b.*
from board b, (select @rownum:=0) as tmp
order by no desc
) as boardlist
where rownum > 1 <= 10

원래의 SQL 코드

public class LoginMapper implements LoginMapper {
//String sql = "select * from member"; // 한 줄은 간편한데 여러 줄이면 불편하다.
String sql = "select *
sql += "from(select @rownum:=@rownum+1 rownum, b.*
sql += " from board b, (select @rownum:=0) as tmp"
}

memreg의 구현 및 연결

LoginService.java -> LoginServiceImpl.java : 추상메서드 memreg를 구현한다. 

LoginMapper.java -> LoginMapper.xml : memreg 연결한다. insert 태그를 써야한다. 

각 memreg 구현 (LoginService.java -> LoginServiceImpl.java) 및 연결 (LoginMapper.java -> LoginMapper.xml)을 알 수 있다.

코드) LoginMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.hj.mapper.LoginMapper">
<!-- #####새로 추가함##### -->
<insert id="memreg"> <!-- LoginMapper.java의 memreg를 연결한다. insert태그를 사용한다.-->
insert into member (id, password, name, birthday, gender)
values(#{id},#{password},#{name},sysdate(),"f");
</insert>
<select id="login" resultType="org.hj.model.LoginVO">
select id, password
from member
where id=#{id} and password=#{password}
</select>
</mapper>

DB의 Insert의 진행 과정

LoginMapper.javaLoginVO member가 

	public void memreg(LoginVO member);

가 아래 LoginMapper.xml"memreg"에 연결되며,

<insert id="memreg"> <!-- LoginMapper.java의 memreg를 연결한다. insert태그를 사용한다.-->
insert into member (id, password, name, birthday, gender)
values(#{id},#{password},#{name},sysdate(),"f");
</insert>

콘솔창에 인출된 값이 

콘솔창 결과

LoginVO [id=id0329, password=5678910, addr=null, phone=null, email=null, name=유자바, age=0]

LoginMapper.xml의 아래 값에 연결된다.

values(#{id},#{password},#{name},sysdate(),"f");

로그인 화면

코드) home.jsp
<input>이나 <a>태그를 사용한다. 

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<meta charset="UTF-8">
<title>Home</title>
<!-- css -->
<!-- javascript -->
</head>
<body>
<h1>
Hello world!
</h1>
Main page입니다.<br><br> <!-- 방식 지정 없으면 GET 방식이다. -->
<input type=button value="로그인 하러가기" onclick="location.href='http://localhost:8081/login'">
<a href="/login">로그인 하러 가기</a>
</body>
</html>

결과) 


데이터의 흐름

login.jsp: 데이터 

-> MemberController.java: member -> member

-> LoginService.javamember

-> LoginServiceImpl.javamember from public LoginVO login (LoginVO member) -> return lm.login(member)


-> LoginMapper.javamember from public LoginVO login(LoginVO member);


-> LoginMapper.xml<select> #{id}, #{password}가 일치해야한다.

그 후 MySqlDB에 저장된다.


Mapper XML Files

코드) LoginMapper.xml 일부
(경로: SpringEx\src\main\resources\org\hj\mapper\LoginMapper.xml)

<select id="login" resultType="org.hj.model.LoginVO"> <!-- 구현메서드가 없으니까 return이 없다. -->
<!-- 대신 select는 resultType이라는 타입이 return 역할이다. -->
select id, password
from member
where id=#{id} and password=#{password}
</select>

구현메서드가 없으니까 return이 없다. 
대신 select는 resultType이라는 타입이 return 역할이다. 

resultType The fully qualified class name or alias for the expected type that will be returned from this statement.

Note that in the case of collections, this should be the type that the collection contains,
not the type of the collection itself.

Use resultType OR resultMap, not both.

참고: https://mybatis.org/mybatis-3/ko/sqlmap-xml.html
참고: https://mybatis.org/mybatis-3/sqlmap-xml.html#mapper-xml-files

코드)
LoginServiceImpl.java
아래 코드는 resultType의 호출을 한다.

return lm.longin(member)
package org.hj.service;
import org.hj.mapper.LoginMapper;
import org.hj.model.LoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
LoginMapper lm;
//#####새로 추가함#####
public void memreg(LoginVO member) {
lm.memreg(member);
};
public LoginVO login (LoginVO member) {
System.out.println("service="+member);
System.out.println("service return="+lm.login(member));
return lm.login(member);
}
}

아래 LoginVO.java를 그대로 return 한다. 

코드) LoginVO.java 일부 

public class LoginVO {
private String id;
private String password;
private String addr;
private String phone;
private String email;
private String name;
private int age;
}

위 데이터는 결국 MemberController.java
session.setAttribute("login", 
의 session에 저장을 한다. (JSP와 동일하다.)

코드) MemberController.java 일부 
다시 수정됐다.

if (ls.login(member)==null) {
return "member/login";
} else {
session.setAttribute("login", ls.login(member)); //null이 아니면 세션에 저장한다.
//return "redirect:/list"; //게시판 리스트 보기 위해 내가 아래 코드로 바꿈.
return "board/list";
}

 

728x90
반응형
Comments