본문 바로가기

IT&코딩/Spring

Spring - 7일차 (dbConfig)

728x90
반응형

* 이번 시간에는 Mapper 를 이용하여 MemberDAO 없이 SQL문을 태그를 활용하여 MVC 모델을 구현하고자 한다.

 

* 기본적인 파일들은 springController에서 가져왔다.

 

* springController와의 차이점은 DbConfig.java 파일과, src/main/resource -> mappers/member 폴더 안에 memberMapper.xml 이라는 XML 파일이 추가되었다.

 

* 의존성 주입 관련 라이브러리도 있으니 pom.xml을 참고할 것. 

 

 

 


 

■ Mapper를 이용한 두 가지 방식

 

□ 첫 번째 방식 : XML을 이용하는 방식 

 

* src/main/java 폴더의 com.exam.dbc 패키지 -> DbConfig.java

 

@Configuration 과 @Bean을 이용한다.

 

package com.exam.dbc;

import java.io.IOException;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

@Configuration
@MapperScan(basePackages = {"com.exam.dbc"})
public class DbConfig {
	
	@Bean // 메서드의 반환 데이터를 Bean으로 Spring에 등록
	public HikariDataSource dataSource() {
		
		HikariConfig hikariConfig = new HikariConfig();
		hikariConfig.setDriverClassName("oracle.jdbc.OracleDriver");
		hikariConfig.setJdbcUrl("jdbc:oracle:thin@localhost:1521:xe");
		hikariConfig.setUsername("spring1212");
		hikariConfig.setPassword("oracle");
		
		HikariDataSource dataSource = new HikariDataSource(hikariConfig);
		
		return dataSource;
		
	}
	
	@Bean
	public SqlSessionFactoryBean sessionFactory() throws IOException{
		
		SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource());
		
		PathMatchingResourcePatternResolver resolver =
				new PathMatchingResourcePatternResolver();
		
		sessionFactory.setMapperLocations(resolver.getResources("classpath:/mappers/**/*Mapper.xml"));
		
		return sessionFactory;
	}
	
}

 

* src/main/resources 폴더의 mappers/member/memberMapper.xml

 

<mapper> 태그를 사용하여 쿼리문을 작성한다.

 

<?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="com.exam.db.IMemberDAO">
	<insert id="insert" parameterType="com.exam.db.MemberDTO" >
		INSERT INTO db_basic VALUES(#{id},#{pw},#{name})
	</insert>
	
	<select id="selectId" 
			parameterType="String" 
			resultType="com.exam.db.MemberDTO">
		SELECT * FROM db_basic WHERE id = #{id}
	</select>
	
	<select id="selectAll"
			resultType="com.exam.db.MemberDTO">
		SELECT * FROM db_basic
	</select>
	
	<update id="update"
			parameterType="com.exam.db.MemberDTO">
			UPDATE db_basic SET pw = #{pw}, name = #{name} WHERE id = #{id}
	</update>
	
	<delete id="delete"
			parameterType="com.exam.db.MemberDTO">
			DELETE FROM db_basic WHERE id = #{id}
	</delete>
</mapper>

 

□ 두 번째 방식 : root-context.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean class="com.zaxxer.hikari.HikariConfig" id="hikariConfig">
		<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
		<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe"/>
		<property name="username" value="spring1212"/>
		<property name="password" value="oracle"/>
	</bean>	
	
	<bean class="com.zaxxer.hikari.HikariDataSource" id="dataSource">
		<constructor-arg ref="hikariConfig"/>
	</bean>
	
	<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sessionFactory">
		<property name="dataSource" ref="dataSource"/>
		<property name="mapperLocations" value="classpath:/mappers/**/*Mapper.xml" />
	</bean>
	
	<!-- Namespaces tab 에서 mybatis-spring check하고 와서 사용해야함. -->
	<mybatis-spring:scan base-package="com.exam.db"/>
</beans>

 


 

■ com.exam.dbc 패키지

 

□ MemberDTO.java (DTO)

 

package com.exam.dbc;

public class MemberDTO {
	
	private String id;
	private String pw;
	private String name;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPw() {
		return pw;
	}
	public void setPw(String pw) {
		this.pw = pw;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

 

□ IMemberDAO.java (DAO)

 

package com.exam.dbc;

import java.util.ArrayList;

import org.springframework.stereotype.Repository;

@Repository
public interface IMemberDAO {
	
	public void insert(MemberDTO member);

	public MemberDTO selectId(String id);

	public ArrayList<MemberDTO> selectAll();
	
	public void delete(MemberDTO deleteMember);

	public void update(MemberDTO updateMember);
	
}

 

□ IMemberService.java (interface)

 

package com.exam.dbc;

import java.util.ArrayList;

public interface IMemberService {
	
	public String insert(String id, String pw, String name);
	
	public void selectId(String id);

	public String delete(String id, String pw, String confirmPw);
	
	public ArrayList<MemberDTO> selectAll();

	public String update(MemberDTO member);
	
}

 

□ MemberServiceImpl.java (Service)

 

package com.exam.dbc;

import java.util.ArrayList;

import javax.servlet.http.HttpSession;

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

@Service
public class MemberServiceImpl implements IMemberService {
	
	@Autowired private IMemberDAO memberDao;
	
	@Override
	public String insert(String id, String pw, String name) {
		if(id == "" || id == null) {
			return "비정상적인 아이디입니다.";
		}
		
		MemberDTO check = memberDao.selectId(id);
		if(check == null) {
			MemberDTO member = new MemberDTO();
			member.setId(id);
			member.setPw(pw);
			member.setName(name);
			memberDao.insert(member);
			
			return "회원 가입 완료";
		}
		return "이미 가입된 정보입니다.";
	}
	
	@Autowired private HttpSession session;
	public String login(String id, String pw) {
		MemberDTO member = memberDao.selectId(id);
		
		if(member == null) {
			return "정보가 존재하지 않습니다.";
		}
		
		if(member.getPw().equals(pw) == false) {
			return "비밀번호가 일치하지 않습니다.";
		}
		
		session.setAttribute("id", member.getId());
		session.setAttribute("name", member.getName());
		
		return "로그인 성공";
	}
	
	@Override
	public void selectId(String id) {
		
		MemberDTO member = memberDao.selectId(id);
		if(member == null) {
			System.out.println("정보가 존재하지 않습니다.");
		} else {
			System.out.println("### 검색 결과 ###");
			System.out.println("아이디 : " + member.getId());
			System.out.println("비밀번호 : " + member.getPw());
			System.out.println("이름 : " + member.getName());
		}
	}

	@Override
	public String delete(String id, String pw, String confirmPw) {
		if(pw.equals(confirmPw) == false) {
			return "비밀번호를 확인한 후 다시 입력하세요.";
		}
		
		MemberDTO deleteMember = memberDao.selectId(id);
		if(deleteMember != null) {
			if(deleteMember.getPw().equals(pw)) {
				memberDao.delete(deleteMember);
				return "회원 삭제 완료";
			}
			return "비밀번호를 정확하게 입력하세요.";
		} else {
			return "정보가 존재하지 않습니다.";
		}
		
	}

	@Override
	public ArrayList<MemberDTO> selectAll() {
		return memberDao.selectAll();
	}

	@Override
	public String update(MemberDTO member) {
		MemberDTO check = memberDao.selectId(member.getId());
		if(check != null) {
			memberDao.update(member);
			return "회원 수정 완료";
		} else {
			return "정보가 존재하지 않습니다.";
		}
	}

}

 

□ MemberController.java (Controller)

 

package com.exam.dbc;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class MemberController {
	
	@Autowired private MemberServiceImpl service; 
	
	@RequestMapping("/index")
	public String index() {
		System.out.println("index() 인덱스 요청이 옴");
		return "index";
	}
	
	@RequestMapping("/")
	public String root() {
		System.out.println("root() 인덱스 요청이 옴");
		return "index";
	}
	
	@RequestMapping(value="login", method = RequestMethod.GET)
	public void login() {
		
	}
	
	@RequestMapping(value = "login", method = RequestMethod.POST)
	public String login(String id, String pw) {
		System.out.println("아이디 : " + id);
		System.out.println("비밀번호 : " + pw);
		
		String msg = service.login(id, pw);
		if(msg.equals("로그인 성공")) {
			return "index";
		} else {
			return "login";
		}
	}
	
	@Autowired private HttpSession session;
	@RequestMapping("logout")
	public String logout() {
		session.invalidate();
		return "index";
	}
	
	@GetMapping("register")
	public String register() {
		return "member/register";
	}
	
	@PostMapping("register")
	public String register(Model model,
			@RequestParam("id")String i,
			@RequestParam("pw")String p,
			@RequestParam("name")String n) {
		
		System.out.println("아이디 : " + i);
		System.out.println("비밀번호 : " + p);
		System.out.println("이름 : " + n);
		
		String msg = service.insert(i, p, n);
		
		model.addAttribute("msg", msg);
		if(msg.equals("회원 가입 완료")) {
			return "index";
		} else {
			return "member/register";
		}
		
	}
	
	@RequestMapping("list")
	public String list(Model model) {
		model.addAttribute("members", service.selectAll());
		return "list";
	}
	
	@GetMapping("update")
	public String update() {
		String id = (String)session.getAttribute("id");
		if(id == null)
			return "login";
		return "update";
	}
	
	@PostMapping("update")
	public String update(MemberDTO member) {
		System.out.println("아이디 : " + member.getId());
		System.out.println("비밀번호 : " + member.getPw());
		System.out.println("이름 : " + member.getName());
		
		member.setId((String)session.getAttribute("id"));
		String msg = service.update(member);
		
		if(msg.equals("회원 수정 완료")) {
			return "index";
		} else {
			return "update";
		}
	}
	
	@GetMapping("delete")
	public String delete() {
		String id = (String)session.getAttribute("id");
		if(id == null)
			return "login";
		return "delete";
	}
	
	@PostMapping("delete")
	public String delete(String pw, String confirmPw, Model model) {
		String id = (String)session.getAttribute("id");
		
		String msg = service.delete(id, pw, confirmPw);
		model.addAttribute("msg", msg);
		
		if(msg.equals("회원 삭제 완료")) {
			return "index";
		} else {
			return "delete";
		}
	}
	
}

 


 

■ jsp 파일

 

□ views/member

 

* register.jsp (회원 등록)

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>register</title>
</head>
<body>
	<c:url value="/" var="contextRoot"/>
	<h3>#{msg }</h3>
	<form action="register" method="post">
		<input type="text" name="id" placeholder="아이디"/><br>
		<input type="password" name="pw" placeholder="비밀번호"/><br>
		<input type="text" name="name" placeholder="이름"/><br>
		<input type="submit" value="회원가입"/><br>
		<input type="button" value="취소" onclick="location.href='${contextRoot}'"/>
	</form>
</body>
</html>

 

□ views

 

* index.jsp

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>index</title>
	<style>
		a{text-decoration: none;}
	</style>
</head>
<body>
	<a href="index">인덱스</a> | 
	<c:choose>
		<c:when test="${empty sessionScope.id }">
			<a href="login">로그인</a> | 
			<a href="register">회원 가입</a>
		</c:when>
		<c:otherwise>
			<a href="logout">로그아웃</a> | 
			<a href="list">회원 목록</a> | 
 			<a href="update">회원 수정</a> | 
			<a href="delete">회원 삭제</a> 
		</c:otherwise>
	</c:choose>
</body>
</html>

 

* login.jsp (로그인)

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
	<!-- 지금 사용 중인 프로젝트의 컨텍스트를 구해서 contextRoot라는 변수에 저장함. -->
	<c:url value="/" var="contextRoot"/>
	
	<form action="login" method="post">
		<input type="text" name="id" placeholder="아이디" /><br>
		<input type="password" name="pw" placeholder="비밀번호" /><br>
		<input type="submit" value="로그인" />
		<input type="button" value="취소" onclick="location.href='${contextRoot }'" />
	</form>
</body>
</html>

 

* list.jsp (회원 목록)

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>list</title>
	<style>
		a{text-decoration: none;}
	</style>
</head>
<body>
<c:choose>
	<c:when test="${empty members}">
		<h3>회원의 정보가 없습니다.</h3>
	</c:when>
	<c:otherwise>
		<table border=1>
			<tr>
				<th>아이디</th>
				<th>비밀번호</th>
				<th>이름</th>
			</tr>
			<c:forEach items="${members }" var="member">
				<tr>
					<td>${member.id }</td>
					<td>${member.pw }</td>
					<td>${member.name }</td>
				</tr>
			</c:forEach>
		</table>
	</c:otherwise>
</c:choose>
<a href="index">인덱스 페이지로 이동</a>
</body>
</html>

 

* update.jsp (회원 수정)

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>update</title>
</head>
<body>
	<c:url value="/" var="contextRoot"/>

	<form action="update" method="post">
		<input type="text" readonly="readonly" value="${sessionScope.id }"/><br>
		<input type="password" name="pw" placeholder="비밀번호" /><br>
		<input type="text" name="name" value="${sessionScope.name }"/><br>
		<input type="submit" value="회원 수정" />
		<input type="button" value="취소" onclick="location.href='${contextRoot }'" />
	</form>
</body>
</html>

 

* delete.jsp

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>delete</title>
<script>
	function check(){
		if(confirm('탈퇴하시겠습니까?'))
			document.getElementById('f').submit();
	}
</script>
<style type="text/css">
	h3{color : red}
</style>
</head>
<body>
	<c:url value="/" var="contextRoot"/>
	<h3>${msg }</h3>
	<form action="delete" method="post" id="f">
		<input type="text" value='${sessionScope.id }' readonly="readonly"><br>
		<input type="password" name="pw" placeholder="비밀번호"><br>
		<input type="password" name="confirmPw" placeholder="비밀번호 확인"><br>
		<input type="button" value="회원 탈퇴" onclick="check();">
		<input type="button" value="취소" onclick="location.href='${contextRoot }'" />
	</form>
</body>
</html>

 

□ pom.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.exam</groupId>
	<artifactId>dbc</artifactId>
	<name>dbConfig2</name>
		<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.2.1.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				 </exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
				
		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>	
		
		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
			</exclusions>
			<scope>runtime</scope>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>
				
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	
		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.7</version>
			<scope>test</scope>
		</dependency>   
		
		<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc8 -->
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<version>21.8.0.0</version>
		</dependency>  
		
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-jdbc</artifactId>
		    <version>${org.springframework-version}</version>
		</dependency>
				
		<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
		<dependency>
		    <groupId>com.zaxxer</groupId>
		    <artifactId>HikariCP</artifactId>
		    <version>4.0.3</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis</artifactId>
		    <version>3.5.11</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis-spring</artifactId>
		    <version>2.1.0</version>
		</dependency>
		   
	</dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
728x90
반응형

'IT&코딩 > Spring' 카테고리의 다른 글

Spring - 9일차 (ajaxConcept)  (0) 2022.12.27
Spring - 8일차 (dbQuiz)  (0) 2022.12.20
Spring - 6일차 (mvc_quiz)  (0) 2022.12.19
Spring - 5일차 (MVC2 모델 실습)  (0) 2022.12.15
Spring - 4일차 (MVC2 모델 이론)  (0) 2022.12.15