본문으로 바로가기
728x90
반응형

이전 포스팅에서 Controller를 생성하고 JSP와 연동까지 했었다.

이번에는 Database를 연동하여 로그인을 할 것이다.


1. pom.xml

Dependency 추가

<!-- Gson -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
</dependency>

<!-- MariaDB -->
<!-- <dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
</dependency> -->

<!-- mysql -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.22</version>
</dependency>

<!-- mybatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>

<!-- log4j2 -->
<dependency>
    <groupId>org.bgee.log4jdbc-log4j2</groupId>
    <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
    <version>1.16</version>
</dependency>

2. application.yml

driver 및 연결 정보 설정 추가

datasource:
    # MARIADB
    #driverClassName: org.mariadb.jdbc.Driver
    #url: jdbc:mariadb://localhost:3306/sample_db?characterEncoding=UTF-8&serverTimezone=UTC

    # MYSQL
    # 기본
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/sample_db?characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: root

3. MyBatis

MyBatis 설정

Spring Boot를 하면서 MyBatis를 연결하려니 패키지 별로 config를 지정하는게 잘 되지않았다.

예를 들어 관리자패키지, 클라이언트패키지가 각각 있을 때 config.xmlapage-config.xml, client-config.xml
형식으로 각각 mapper.xml을 참조하도록 하고 싶었으나 되지않았다. 기본적으로 config.xml은 1개만 지정할 수 있는 것 같다.

(구글링해보니 직접적인 config.xml의 분리는 되지 않으나 Java config로 바꾸면 가능하다는 글을 봤지만 어려워서 패스)

그리고 application.yml에서 mybatis 옵션 중 config-location을 추가하면 sqlSessionFactory에서 찾지 못한다는 에러가
발생하여 Java로 별도의 Config를 생성한다.

MyBatisConfig.java

우선 MyBatisConfig.java 파일을 만들어 다음과 같이 작성한다.
(application.yml 에서 datasource를 작성하지 않고 Java에서 작성해도 무방하나 글쓴이는 datasource는 여기서 쓰지않음)

package com.sample;

import java.io.IOException;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisConfig {

    @Autowired
    ApplicationContext applicationContext;

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws IOException {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        // classpath는 현재 src/main/resources 이다
        factoryBean.setConfigLocation(applicationContext.getResource("classpath:/mybatis/mybatis-config.xml"));
        factoryBean.setMapperLocations(applicationContext.getResources("classpath:/mybatis/mapper/**/*.xml"));
        return factoryBean;
    }

    @Bean
    public SqlSessionTemplate sqlSession(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

DataSourceSqlSession을 각각 Bean으로 지정한다.
DataSource에서는 MyBatis설정 파일Mapper 경로를 지정해준다.


mybatis-config.xml

application.yml 파일이 있는 경로에 mybatis/mybatis-config.xml 을 생성

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <typeAliases>
        <typeAlias alias="ApageManageVo" type="com.sample.apage.manage.service.ApageManageVo" />
    </typeAliases>

</configuration>

application.yml 파일이 있는 경로에 mybatis/mapper/apage/ApageManageMapper.xml 을 생성

ApageManageMapper.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.sample.apage.manage.service.impl.ApageManageMapper">

    <!-- 관리자 로그인 -->
    <select id="selectMemberByUserIdAndUserPw" parameterType="ApageManageVo" resultType="ApageManageVo">
        SELECT seq, userId, userPw, userPwEnc, name, tel, phone, email, zipCode, addr1, addr2, remoteIp, level
        FROM adm_member
        WHERE userId=#{userId}
        AND userPw=#{userPw}
    </select>

</mapper>

주의할 점은 mappernamespace 경로를 전체경로로 적어야한다.
(config에서 alias 설정이 가능하면 그렇게 해도된다.

난 안되던데..

)


ApageManageVo.java

다음 Lombok을 이용한 Vo를 작성

package com.sample.apage.manage.service;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
public class ApageManageVo {

    private String seq; // 일련번호
    private String userId; // 아이디
    private String userPw; // 비밀번호
    private String userPwEnc; // 비밀번호 암호화 SHA2-256
    private String name; // 이름
    private String tel; // 전화번호
    private String phone; // 휴대폰
    private String email; // 이메일
    private String zipCode; // 우편번호
    private String addr1; // 대표주소
    private String addr2; // 상세주소
    private String remoteIp; // 접근IP
    private String accessIp; // 허용IP 문자열
    private String level; // 관리자 권한
    private String regDate; // 등록일
    private String editDate; // 수정일
    private String lastLoginDate; // 마지막 로그인 일시
}

@Data 보다 필요한 속성만 쓰는게 좋은 듯하다.


ApageManageService.java

다음 Impl과 연결할 interface를 만든다.

기존에 쓰던 방식은

Controller -> Service -> ServiceImpl -> DAO -> DB

였다면 Spring Boot 에서는

Controller -> Service -> ServiceImpl -> Mapper -> DB

방식인 듯하다. (자세하게 얘기하면 흐름이 길어서 패스)

package com.sample.apage.manage.service;

import javax.servlet.http.HttpServletRequest;

public interface ApageManageService {

    // 관리자 정보
    public ApageManageVo selectMemberByUserIdAndUserPw(ApageManageVo vo) throws Exception;

}

ApageManageServiceImpl.java

package com.sample.apage.manage.service.impl;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;

import com.sample.apage.manage.service.impl.ApageManageMapper;
import com.sample.apage.manage.service.ApageManageVo;
import com.sample.common.utils.NUtil;

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

@Service
public class ApageManageServiceImpl implements ApageManageService {

    @Autowired
    ApageManageMapper mapper;

    @Override
    public ApageManageVo selectMemberByUserIdAndUserPw(ApageManageVo vo) throws Exception {
        return mapper.selectMemberByUserIdAndUserPw(vo);
    }

}

ApageManageMapper.java

package com.sample.apage.manage.service.impl;

import java.util.HashMap;

import com.sample.apage.manage.service.ApageManageVo;

import org.apache.ibatis.annotations.Mapper;

// Mapper 지정 필수
// 여기서 사용되는 메서드의 이름은 Mapper.xml 에서의 쿼리별 id값이된다. (틀리면안됨)
@Mapper
public interface ApageManageMapper {

    // 관리자 정보
    public ApageManageVo selectMemberByUserIdAndUserPw(ApageManageVo vo) throws Exception;

}

ApageManageController.java

다음 ApageManageController.java를 아래와 같이 작성한다.

package com.sample.apage.manage.web;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.google.gson.Gson;
import com.sample.apage.manage.service.ApageManageService;
import com.sample.apage.manage.service.ApageManageVo;
import com.sample.common.utils.NUtil;
import com.sample.common.utils.ShaEncryption;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class ApageManageController {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    private ApageManageService ApageManageService;

    /**
     * 관리자 로그인페이지
     * 
     * @param request
     * @param response
     * @param session
     * @param model
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/apage.do")
    public String adminLogin(HttpServletRequest request, HttpServletResponse response, HttpSession session,
            ModelMap model) throws Exception {

        ApageManageVo mv = (ApageManageVo) session.getAttribute("adminInfo");

        if (mv != null && !mv.getSeq().equals("")) {
            // 로그인한 상태라면 메인페이지로 이동
            return "redirect:/apage/index.do";
        }

        return "apage/apageLogin";
    }

    /**
     * 관리자 로그인
     * 
     * @param request
     * @param response
     * @param session
     * @param vo
     * @throws Exception
     */
    @ResponseBody
    @RequestMapping(value = "/apage/adminLoginJson.do")
    public void adminLoginJson(HttpServletRequest request, HttpServletResponse response, HttpSession session,
            @ModelAttribute("vo") ApageManageVo vo) throws Exception {

        Gson gson = new Gson();
        Map<String, Object> data = new HashMap<String, Object>();

        try {

            log.info("adminLoginJson.do ::: {}", vo);

            ApageManageVo avo = ApageManageService.selectMemberByUserIdAndUserPw(vo);

            if (avo != null) {
                // 로그인정보 세션에 저장
                session.setAttribute("adminInfo", avo);

                // 로그인 정보 일치
                data.put("rs", "1");

            } else {
                // 로그인 정보 불일치
                data.put("rs", "2");
            }

            response.getWriter().print(gson.toJson(data));

        } catch (Exception e) {
            e.printStackTrace();
            response.getWriter().print(gson.toJson(data));
        }

    }

   /**
     * 관리자 메인 페이지
     */
    @RequestMapping(value = "/apage/index.do", method = RequestMethod.GET)
    public String requestMethodName(HttpServletRequest request, HttpServletResponse response, HttpSession session,
            @ModelAttribute("vo") ApageManageVo vo) throws Exception {

        return "apage/index";
    }

}

apageLogin.jsp

로그인 페이지를 간단하게 작성

<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko" lang="ko">
    <head>
        <link href="/resources/favicon.ico?v=2" type="image/x-icon" rel="shortcut icon" />
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>관리자로그인</title>
        <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
        <script type="text/javascript">
            $(document).ready(function () {
                $("#userId").focus();
            });

            function doLogin() {
                if ($("#userId").val() == "" || $("#userId").val() == null) {
                    alert("아이디는 필수 입력 항목입니다.");
                    return;
                }

                if ($("#userPw").val() == "" || $("#userPw").val() == null) {
                    alert("패스워드는 필수 입력 항목입니다.");
                    return;
                }

                $.ajax({
                    type: "POST",
                    url: "<c:url value='/apage/adminLoginJson.do'/>",
                    data: {
                        userId: $.trim($("#userId").val()),
                        userPw: $.trim($("#userPw").val()),
                    },
                    dataType: "json",
                    success: function (data) {
                        if (data.rs == "1") {
                            location.replace("<c:url value='/apage/index.do'/>");
                        } else {
                            alert("아이디 또는 패스워드를 확인하세요.");
                        }
                    },
                    error: function (data, status, err) {
                        alert("로그인에 실패하였습니다.");
                    },
                });
            }
        </script>
    </head>
    <body class="pg-log">
        <div class="log-panel">
            <h1>
                <span>관리자 시스템</span>
            </h1>
            <fieldset class="log-frm">
                <legend>관리자 로그인</legend>
                <p>
                    <label for="userId"><input type="text" id="userId" style="ime-mode: disabled" placeholder="아이디" title="아이디 입력" /></label>
                </p>
                <p>
                    <label for="password"
                        ><input
                            type="password"
                            id="userPw"
                            placeholder="비밀번호"
                            title="비밀번호 입력"
                            onkeypress="if(event.keyCode =='13'){doLogin()}"
                    /></label>
                </p>
                <input type="submit" value="로그인" onclick="doLogin();" />
            </fieldset>
            <p class="log-foot">
                <span>ⓒCopyright(c) 2020. All rights reserved.</span>
            </p>
        </div>
        <!-- //log-panel -->
    </body>
</html>

index.jsp

로그인 후 Hello World 출력할 임시 페이지

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
  <body>
    <h1>Hello World !!!</h1>
  </body>
</html>

실행결과

임의의 admin 계정을 DB에 직접적으로 밀어넣고 로그인을 테스트했다.

  • sample_db

sample_db.sql
3.1 kB

  • 실행된 쿼리

  • 쿼리 조회 결과

  • 호출된 index.jsp 페이지


HikariCP

HikariCP를 적용시켜보자. HikariCP를 쓰는 이유는 속도가 아주 빠르다고한다.

그러면 일단 쓰고봐야지..

이전에 작성했던 application.yml에서 다음과 같이 수정 및 추가한다.

  • Spring Boot에서는 HikariCP가 이미 starter jdbc에 같이 들어있어서 따로 의존성 추가할 필요 없다.
datasource:
    # MARIADB
    #driverClassName: org.mariadb.jdbc.Driver
    #url: jdbc:mariadb://localhost:3306/sample_db?characterEncoding=UTF-8&serverTimezone=UTC

    # MYSQL
    # 기본
    # driverClassName: com.mysql.cj.jdbc.Driver
    # url: jdbc:mysql://localhost:3306/sample_db?characterEncoding=UTF-8&serverTimezone=UTC
    # username: root
    # password: root

    # MYSQL
    # log4j, Hikari 응용
    driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
    url: jdbc:log4jdbc:mysql://localhost:3306/sample_db?characterEncoding=UTF-8&serverTimezone=UTC

    # Hikari
    hikari.username: root
    hikari.password: root
    hikari.maximum-pool-size: 10
    hikari.connection-test-query: SELECT 1

logback에 대한 내용은 다음 포스팅에서 설명한다.


Previous Chapter

Next Chapter

728x90
반응형