AWS 클라우드환경 네이티브 수업 118 일차
진행
1. 쇼핑몰 제작 - 로그인 기능 구현
요약
1. @Component와 @Bean
2. 쇼핑몰 제작 - 로그인 기능 구현
3. Mapper 세팅
@Component와 @Bean
(1) @Component
직접 작성한 Class를 Bean으로 등록하기 위한 어노테이션
@Configuration 의 하위 어노테이션으로
사용법은 아래와 같다.
@Component(value="hiStudent")
public class Student{
public Student(){
System.out.print("hi");
}
}
value 의 값이 Bean id로 등록이 된다.
만약 value가 없다면 Class의 이름이 CamelCase 로 Bean id로 등록이 된다
(2) @Bean
직접 제어가 불가능한 라이브러리 등을 Bean으로 등록할 때 사용하는 어노테이션
반환되는 객체를 Bean으로 등록한다.
사용법은 아래와 같다.
@Configuration
public class Student{
@Bean(name="hiStudent")
public ArrayList<String> arrayStudent(){
return new ArrayList <String>();
}
}
@Configuration은 스프링 컨테이너에 해당 Class가 Bean으로 등록 될 것이라 선언하는 어노테이션이다.
클래스를 해당 어노테이션으로 등록하고
하위 메소드에 @Bean 으로 반환 객체를 Bean으로 등록한다.
Bean id는 @Component랑은 다르게 value가 아닌 name을 사용하여 입력한다.
Bean id가 없다면 메소드 이름이 CamelCase 로 Bean id로 등록이 된다
(3) 위의 어노테이션을 사용하기 위한 공통 설정 (xml 파일설정)
bean을 등록하는 servlet-context.xml 에서
하단의 Namespaces 탭으로 들어가서 context 쪽을 체크한다.
이후 다시 Source 탭에서
<context:component-scan base-package="com.test.webtest" />
base-package 속성 값으로 어노테이션들을 체크할 패키지를 적어 넣는다.
그럼 해당 패키지 내부 모든 어노테이션들을 확인해서 Bean으로 등록할 것들을 다 등록하게 된다.
쇼핑몰 제작 - 로그인 기능 구현
(1) VO
// MemberVO.java
package com.edu.member.vo;
import java.sql.Date;
import org.springframework.stereotype.Component;
import lombok.Data;
//========================================
// 회원 정보 VO = Value Object (값만 싣는다 setter안씀)
// DTO (getter setter 씀)
// @Component 어노테이션을 이용하면 Bean Configuration 파일에 Bean으로 따로 등록하지 않아도
// 사용할 수 있다.
// @Component 어노테이션은 기본적으로 타입기반의 자동주입 어노테이션이다.
//========================================
@Component("memberVO")
@Data
public class MemberVO {
private String id; // 사용자 아이디
private String pw; // 사용자 비밀번호
private String name; // 사용자 이름
private String email; // 사용자 이메일
private Date joinDate; // 가입일자
public MemberVO() {} // 기본생성자
public MemberVO(String id, String pw, String name, String email) {
this.id = id;
this.pw = pw;
this.name = name;
this.email = email;
}
} // End - public class MemberVO
lombok의 @Data 어노테이션을 사용하게 되면
Java에서 기본적으로 생성자 설정이 안되었을 시 생성자를 자동 생성해주는 것이 활성화가 안된다.
따라서, lombok을 사용하면 생성자는 따로 생성을 해주어야 에러가 발생하지 않는다.
그리고 @NoArgsConstructor 어노테이션을 사용하면
위의 코드에서 생성자 메소드 구문도 필요가 없게 된다.
(2) DAO
Mapper 세팅에 관한 설명은 맨 아래를 참조.
<!-- mapper 생성 (memberMapper.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="com.edu.member">
<!-- 로그인정보 가져오기 -->
<resultMap id="memberResult" type="com.edu.member.vo.MemberVO">
<result property="id" column="id"/>
<result property="pw" column="pw"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<result property="joinDate" column="joinDate"/>
</resultMap>
<!-- 로그인 처리 -->
<!-- #{id} = #{MemberVO.id} -->
<select id="loginByID" parameterType="com.edu.member.vo.MemberVO" resultType="com.edu.member.vo.MemberVO">
<![CDATA[
SELECT * FROM T_MEMBER WHERE id = #{id}
]]>
</select>
</mapper>
<mapper > 안의 namespace 는 이 mapper가 해당 위치로 가상으로 있는 것처럼 해준다.
<resultMap> 태그의 id는 해당 내용을 가진 객체의 변수 이름
type은 해당 객체의 type을 말한다.
위의 경우, memberResult 라는 이름의 MemberVO 타입을 가지는 객체인데
property는 자바 쪽에서 사용하는 변수 이름이고 거기에 대응 되는
column 값이 데이터베이스에 설정된 column 값과 동일하면 1:1 대응이 된다.
(만약 데이터 베이스의 join_date1 이 항목의 이름이라면 위에서는 column="join_date1"가 되어야 한다)
<select> 태그의 id는 마찬가지로 변수 이름이고
para는 말그대로 매개변수, DAO -> DB 전달되는 데이터형
resultType은 반환하는 변수 타입, DB -> DAO로 전달되는 데이터형을 말한다.
여기서는 안의 쿼리 내용에 부합하는 데이터를 MemberVO 타입으로 반환한다는 의미가 되겠다.
// DAO 인터페이스 생성
package com.edu.member.dao;
import org.springframework.dao.DataAccessException;
import com.edu.member.vo.MemberVO;
//========================================================
// 회원정보 DAO
//========================================================
public interface MemberDAO {
//========================================================
// 로그인 처리
//========================================================
public MemberVO loginByID(MemberVO memberVO) throws DataAccessException;
} // End - public interface MemberDAO
// DAO 생성
package com.edu.member.dao;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Repository;
import com.edu.member.vo.MemberVO;
//========================================================
// 회원정보 DAO
//========================================================
@Repository("memberDAO")
public class MemberDAOImpl implements MemberDAO {
@Autowired
private SqlSession sqlSession;
private static final String Namespace = "com.edu.member";
//========================================================
// 로그인 처리
//========================================================
@Override
public MemberVO loginByID(MemberVO memberVO) throws DataAccessException {
// Namespace.loginByID = 이름(Mapper에서 실행시킬넘), memberVO = 넘겨줄 값(id, pw)
MemberVO memVO = sqlSession.selectOne(Namespace + ".loginByID", memberVO);
return memVO;
}
} // End - public class MemberDAOImpl implements MemberDAO
sqlSession의 경우, root-context.xml에서 Bean 객체로 이미 선언이 되어 있다.
해당 객체를 @Autowired로 가져오는 것.
mapper의 가상위치를 변수로 저장하고
sqlSession.selectOne("select 태그의 쿼리문 아이디", select 태그의 ParameterType);
위와 같은 구조이므로 selectOne()으로 가져와서 저장되는 변수 memVO는 가져올 변수와 타입이 동일해야 한다.
selectOne은 하나의 객체만을 반환해준다.
selectList는 여러 객체 반환을 하려면 쓴다.
(3) Service
// 서비스 인터페이스 생성
package com.edu.member.service;
import org.springframework.dao.DataAccessException;
import com.edu.member.vo.MemberVO;
//===================================================
// 회원정보 서비스
//===================================================
public interface MemberService {
//===================================================
// 로그인 처리
//===================================================
public MemberVO login(MemberVO memberVO) throws DataAccessException;
} // End - public interface MemberService
// 서비스 생성
package com.edu.member.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import com.edu.member.dao.MemberDAO;
import com.edu.member.vo.MemberVO;
//========================================================
// 회원정보 서비스
//========================================================
@Service("memberService")
public class MemberServiceImpl implements MemberService {
private static final Logger logger = LoggerFactory.getLogger(MemberServiceImpl.class);
@Autowired
private MemberDAO memberDAO;
//===================================================
// 로그인 처리
//===================================================
@Override
public MemberVO login(MemberVO memberVO) throws DataAccessException {
logger.info("MemberServiceImpl login() 시작...");
return memberDAO.loginByID(memberVO);
}
}
(4) Controller
// 컨트롤러 인터페이스 생성
package com.edu.member.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.edu.member.vo.MemberVO;
//=====================================================
// public interface MemberController
//=====================================================
public interface MemberController {
//=====================================================
// 로그인 처리
//=====================================================
public ModelAndView login(@ModelAttribute("member") MemberVO member, RedirectAttributes rAttr,
HttpServletRequest request, HttpServletResponse response) throws Exception;
} // End - public interface MemberController
// 컨트롤러 생성
package com.edu.member.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.edu.member.service.MemberService;
import com.edu.member.vo.MemberVO;
//================================================================
// 회원정보 컨트롤러
//================================================================
@Controller("memberController")
@RequestMapping("/member") // url에서 /member로 시작하는 요청들을 처리하는 컨트롤러
public class MemberControllerImpl implements MemberController {
private static final Logger logger = LoggerFactory.getLogger(MemberControllerImpl.class);
//================================================================
// @Inject : Java에서 지원하는 어노테이션... 특정 Framework에 종속적이지 않다.
// @Autowired : Spring에서 지원하는 어노테이션...
//================================================================
@Autowired
private MemberVO memberVO;
// MemberService memberService = new MemberService();
@Autowired
private MemberService memberService;
//================================================================
// 로그인 처리
//================================================================
@Override
@RequestMapping(value="/login.do", method=RequestMethod.POST)
public ModelAndView login(@ModelAttribute("member") MemberVO member, RedirectAttributes rAttr, HttpServletRequest request,
HttpServletResponse response) throws Exception {
logger.info("MemberControllerImpl login() 시작...");
System.out.println("로그인 정보 => " + member.getId() + " : " + member.getPw());
ModelAndView mav = new ModelAndView();
// 로그인한 정보를 가지고 데이터베이스에 존재하는지 처리를 하고 그 결과를 가져온다.
// 보여줄 화면 종류 = 데이터가 제대로 일치할때, 회원이 존재하지 않을때, 회원은 존재하는데 다른 정보가 틀렸을때
memberVO = memberService.login(member);
System.out.println("로그인 처리 결과 => " + memberVO);
// 로그인한 정보가 데이터베이스에 존재하는지에 따라 처리를 다르게 한다.
if(memberVO != null) { // 로그인 정보에 해당하는 자료가 있으면
if(member.getPw().equals(memberVO.getPw())) {
// 아이디와 비밀번호가 일치하면 세션을 발급한다.
HttpSession session = request.getSession();
session.setAttribute("member", memberVO);
session.setAttribute("isLogOn", true);
// mav.setViewName("redirect:/member/listMembers.do");
mav.setViewName("redirect:/"); // 메인화면으로 이동
} else { // 아이디는 있는데 비밀번호가 틀린 경우
rAttr.addAttribute("result", "비밀번호가 틀렸습니다.");
mav.setViewName("redirect:/member/loginForm.do");
}
} else { // 로그인한 아이디가 존재하지 않으면
// 로그인 실패 메시지를 가지고 로그인 화면으로 이동한다.
rAttr.addAttribute("result", "아이디가 존재하지 않습니다.");
mav.setViewName("redirect:/member/loginForm.do");
}
return mav;
}
} // End - public class MemberControllerImpl implements MemberController
(5) topMenu.jsp
<!-- id와 pw가 사용되는 jsp 파일코드(topMenu.jsp) -->
<div class="form-group">
<input type="text" class="form-control" name="id" size="12" maxlength="12" placeholder="아이디"/>
<input type="password" class="form-control" name="pw" size="12" maxlength="12" placeholder="비밀번호"/>
</div>
<c:when test="${isLogOn == true && member != null }">
<p class="navbar-text"><strong>${member.name}</strong>님 안녕하세용~</p>
<a href="${contextPath}/member/logout.do" class="btn btn-warning">
<span class="glyphicon glyphicon-log-out"></span>로그아웃
</a>
</c:when>
완료 화면
로그인 하기 전
로그인 한 후
INFO : com.edu.member.controller.MemberControllerImpl - MemberControllerImpl login() 시작...
로그인 정보 => id1 : 1234
INFO : com.edu.member.service.MemberServiceImpl - MemberServiceImpl login() 시작...
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
INFO : jdbc.connection - 1. Connection opened
INFO : jdbc.audit - 1. Connection.new Connection returned
INFO : jdbc.audit - 1. Connection.isClosed() returned false
INFO : jdbc.audit - 1. Connection.getAutoCommit() returned true
INFO : jdbc.audit - 1. Connection.getAutoCommit() returned true
INFO : jdbc.audit - 1. PreparedStatement.new PreparedStatement returned
INFO : jdbc.audit - 1. Connection.prepareStatement(SELECT * FROM T_MEMBER WHERE id = ?) returned net.sf.log4jdbc.sql.jdbcapi.PreparedStatementSpy@48dc55db
INFO : jdbc.audit - 1. PreparedStatement.setString(1, "id1") returned
INFO : jdbc.sqlonly - SELECT * FROM T_MEMBER WHERE id = 'id1'
INFO : jdbc.sqltiming - SELECT * FROM T_MEMBER WHERE id = 'id1'
{executed in 15 msec}
INFO : jdbc.audit - 1. PreparedStatement.execute() returned true
INFO : jdbc.resultset - 1. ResultSet.new ResultSet returned
INFO : jdbc.audit - 1. PreparedStatement.getResultSet() returned net.sf.log4jdbc.sql.jdbcapi.ResultSetSpy@78fb39ba
INFO : jdbc.resultset - 1. ResultSet.getMetaData() returned com.mysql.cj.jdbc.result.ResultSetMetaData@3c5af176 - Field level information:
com.mysql.cj.result.Field@1a65c4b1[dbName=gils,tableName=T_MEMBER,originalTableName=t_member,columnName=id,originalColumnName=id,mysqlType=253(FIELD_TYPE_VARCHAR),sqlType=12,flags= PRIMARY_KEY, charsetIndex=255, charsetName=UTF-8]
com.mysql.cj.result.Field@6b6ba44b[dbName=gils,tableName=T_MEMBER,originalTableName=t_member,columnName=pw,originalColumnName=pw,mysqlType=253(FIELD_TYPE_VARCHAR),sqlType=12,flags=, charsetIndex=255, charsetName=UTF-8]
com.mysql.cj.result.Field@77d5f31[dbName=gils,tableName=T_MEMBER,originalTableName=t_member,columnName=name,originalColumnName=name,mysqlType=253(FIELD_TYPE_VARCHAR),sqlType=12,flags=, charsetIndex=255, charsetName=UTF-8]
com.mysql.cj.result.Field@6cc7dfe9[dbName=gils,tableName=T_MEMBER,originalTableName=t_member,columnName=email,originalColumnName=email,mysqlType=253(FIELD_TYPE_VARCHAR),sqlType=12,flags=, charsetIndex=255, charsetName=UTF-8]
com.mysql.cj.result.Field@58b3ce8c[dbName=gils,tableName=T_MEMBER,originalTableName=t_member,columnName=joindate,originalColumnName=joindate,mysqlType=10(FIELD_TYPE_DATE),sqlType=91,flags= BINARY, charsetIndex=63, charsetName=ISO-8859-1]
INFO : jdbc.resultset - 1. ResultSet.getType() returned 1003
INFO : jdbc.resultset - 1. ResultSet.isClosed() returned false
INFO : jdbc.resultset - 1. ResultSet.next() returned true
INFO : jdbc.resultset - 1. ResultSet.getString(id) returned id1
INFO : jdbc.resultset - 1. ResultSet.getString(pw) returned 1234
INFO : jdbc.resultset - 1. ResultSet.getString(name) returned name1
INFO : jdbc.resultset - 1. ResultSet.getString(email) returned test@test.com
INFO : jdbc.resultset - 1. ResultSet.getDate(joindate) returned 2022-11-10
INFO : jdbc.resultset - 1. ResultSet.isClosed() returned false
INFO : jdbc.resultsettable -
|----|-----|------|--------------|-----------|
|id |pw |name |email |joindate |
|----|-----|------|--------------|-----------|
|id1 |1234 |name1 |test@test.com |2022-11-10 |
|----|-----|------|--------------|-----------|
INFO : jdbc.resultset - 1. ResultSet.next() returned false
INFO : jdbc.resultset - 1. ResultSet.close() returned void
INFO : jdbc.audit - 1. PreparedStatement.getConnection() returned net.sf.log4jdbc.sql.jdbcapi.ConnectionSpy@2d371949
INFO : jdbc.audit - 1. Connection.getMetaData() returned com.mysql.cj.jdbc.DatabaseMetaDataUsingInfoSchema@6e44d8fb
INFO : jdbc.audit - 1. PreparedStatement.getMoreResults() returned false
INFO : jdbc.audit - 1. PreparedStatement.getUpdateCount() returned -1
INFO : jdbc.audit - 1. PreparedStatement.close() returned
INFO : jdbc.audit - 1. Connection.isClosed() returned false
INFO : jdbc.audit - 1. Connection.getAutoCommit() returned true
로그인 처리 결과 => MemberVO(id=id1, pw=1234, name=name1, email=test@test.com, joinDate=2022-11-10)
INFO : com.edu.main.HomeController - Welcome home! The client locale is ko_KR.
위는 로그 기록.
로그인 과정이 어떻게 진행되는지 볼 수 있음.
Controller.java - Service.java - DAO.java - Mapper.xml 구조 로 프로젝트 생성 시
Mapper 세팅
<!-- root-context.xml (applicationContext.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
<!--
<property name="dirverClass" value="com.mysql.cj.jdbc.Driver"></property>
-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/awsdb?useSSL=false&serverTimezone=Asia/Seoul"></property>
<property name="username" value="awsuser"></property>
<property name="password" value="1111"></property>
</bean>
<!-- SqlSessionFactory는 DB와의 연결과 SQL의 실행에 대한 모든 것을 가진 객체 -->
<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml"></property>
</bean>
<!-- SqlSession 객체 주입 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
<constructor-arg name="sqlSessionFactory" ref="SqlSessionFactory"></constructor-arg>
</bean>
</beans>
(1) 위 파일에서 아래처럼 Mapper.xml의 위치를 설정
value 값은 *Mapper.xml이 있는 위치로 되어있다.
<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml"></property>
(2) sqlSession 을 bean으로 선언
<!-- SqlSession 객체 주입 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
<constructor-arg name="sqlSessionFactory" ref="SqlSessionFactory"></constructor-arg>
</bean>
sqlSession
- 개발자들이 DAO와 DB를 직접 연결 맺고 종료할 필요가 없게 해줌.
- 기본적인 트랜잭션 관리나 쓰레드 처리의 안정성 등을 보장
댓글