본문 바로가기
SPRING

[Spring] MyBatis를 활용한 Login 구현 - 2

by 킹명주 2022. 7. 6.

https://mjoo1106.tistory.com/entry/Spring-MyBatis%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Login-%EA%B5%AC%ED%98%84-1

 

[Spring] MyBatis를 활용한 Login 구현 - 1

앞시간에 REST API를 맛만보았다. 원래 프로그래밍을 가장 처음하면 Hello World를 출력하는 것으로 시작한다. 그렇다면 웹에서 시작은 무엇일까? 바로 로그인이다. 이것도 매우 간단하기 때문에 후

mjoo1106.tistory.com

지난번에 mysql 설치를 진행했고 login table을 하나 만들었다. 이를 mybatis를 사용하여 불러와 로그인 페이지를 직접 구현해보고자 한다.

login page

login page는 진짜 간단하게 구현했고 back-end의 api를 호출하기 위해 ajax를 사용했다. 아래 코드를 그대로 따라 붙이면 될 것이다.

<!DOCTYPE html>
<html>
    <head>
        <title>kigmj : login test</title>
        <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    </head>
    <body>
        ID :
        <input type="text" id="id">
        <br>
        PWD :
        <input type="text" id="pwd">
        <br>
        <button onClick="login_test()">login</button>
    </body>
    <script>
        function login_test(){
            var id=document.getElementById("id").value
            var pwd=document.getElementById("pwd").value
            $.ajax({
                    url: 'http://localhost:8080/login',
                    type: 'GET',
                    data: {'id':id,
                            'pwd':pwd
                    },
                    success: (res) => {
                        if(res=="YES")
                            alert("Login Success")
                        else
                            alert("Login Fail")      
                    },
                    error:(log)=>{alert("Connect Fail")}
                })
        }
    </script>
</html>

MyBatis 생성

 

Spring 프로젝트를 사용하기 위해 다시 https://start.spring.io/ 이 사이트에 접속한다.

여기서 Lombok을 추가하게 되는데 아직 감은 안오지만 간단하게 정의하면 Lombok은 Java 기반에서 기계적으로 작성하는 VO, DTO, Entity 관련 작업을 쉽게 할 수 있게 해주는 라이브러리로 Getter, Setter, ToString, hashCode 등의 메소드들을 간편하게 사용할 수 있게 해주는 것이다.


SQL 연결하기

 

bulid.gradle의 dependencies에서 반드시 implementation 'mysql:mysql-connector-java'를 추가해야한다. 이게 없어서 cannot load driver class: com.mysql.cj.jdbc.driver 오류가 뜨면서 한시간 정도 트러블 슈팅한 것같다.

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
	implementation 'mysql:mysql-connector-java'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

 

가장 먼저 mysql과 연결해야 할 것이다. application.properties에 다음과 같이 입력한다.

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&userUnicode=true&characterEncoding=utf8&userSSL=false
spring.datasource.username=root
spring.datasource.password=your_password
mybatis.mapper-locations:classpath:mapper/*.xml

url에서 에러가 많이 나는데 앞선 과정에서 포트를 건들지 않고 로컬환경이라면 localhost:3306 or 127.0.0.1을 입력한다 그 뒤 /test는 database의 이름을 의미한다. 그 다음 serverTimezone, unicode는 부가적인 설정으로 넣어도 되고 안넣어도 된다.

 

간단한 controller를 만들어서 error가 없는지 테스트한다.

package com.example.dbtest.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @RequestMapping(value="/", method= RequestMethod.GET)
    public String home() {
        return "Hello World!";
    }
}

위 사진처럼 컴파일에 성공하면 mysql과 연결에 성공한 것이다.

이제 본격적으로 앞서 만든 login page의 ajax를 통해 받은 값들과 데이터베이스에 저장된 데이터를 비교하여 맞는지 틀린지 비교하는 코드를 작성해보자.


Login 구현

 

login의 경우 id와 pwd를 입력하게 되면 login table에서 확인 후 결과 값을 전송해주는 구조로 작성했다. 그러므로 우선적으로 ajax가 스프링이랑 잘 통신하고 있는지 API TEST를 해보자!

앞서만든 controller 패키지의 TestController를 아래와 같이 수정한다.

package com.example.dbtest.controller;

import com.example.dbtest.service.TestService;
import org.springframework.web.bind.annotation.*;

@RestController
@CrossOrigin("*")
public class TestController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home() {
        return "Hello World!";
    }

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public void home(@RequestParam(value = "id") String id, @RequestParam(value = "pwd") String pwd) {
        System.out.println(id+", "+pwd);

    }
}

login으로 get 요청이 들어온 경우 System.out.println으로 잘 받았는지 테스트하는 코드이다. 그런데 여기서 @CrossOrigin("*")은 무엇일까??

앞서 설계한 login.html은 서버 없이 동작하기 때문에 CROS라는 오류가 1000% 확률로 등장할 것이다. 이를 방지하기 위한 선언이다. 이제 테스트를 진짜 해보자. login.html을 열고 아이디와 비밀번호를 입력후 login 버튼을 클릭하면

 

 

 

java server에서도 성공적으로 값을 받은 것을 확인할 수 있다. 이제 웹에서 입력 받은 id 값을 DB에서 조회하여 password를 가져오고 이를 웹에서 입력 받은 password 값과 비교하여 맞으면 YES, 아니라면 NO를 전달해줄 계획이다.

 

위 사진에서 갑자기 mapper와 service 그리고 TestMapper.xml이 생긴 것을 확인할 수 있다. 이는 Spring의 MVC 구조를 따른 것인데

각 요소를 살펴보자.

1. Controller 

Controller는 웹 브라우저의 요청을 전담하여 처리한다.

2. Service

Service는 실질적인 logic을 수행한다.

데이터베이스에 접근하는 DAO를 이용해서 결과값을 받아온다.,

3. DAO

DAO는 데이터베이스에 접속하여 logic에 필요한 쿼리를 호출한다.

4. DB

DB에서 알맞은 쿼리를 실행하고 결과값을 반환한다.

5. DTO 

Data Transfer Object의 약자로

각 계층이 데이터를 주고 받을 때 사용하는 객체를 말한다.

 

MyBatis의 경우 일반적으로 쿼리를 조회할 수 있는 .xml 를 생성하고 Mapper와 Service에서 이를 활용하는 방식이다 그러므로 다음과 같은 구조가 되도록 설계한다.

여기서 mapper와 service에서 class가 아닌 interface로 파일을 생성한 것을 확인할 수 있다. 일반적인 구조에서 자식 클래스가 여러 부모 클래스를 상속받을 수 있다면, 다양한 동작을 수행할 수 있다는 장점을 가지게 될 것이다. 하지만 클래스를 이용하여 다중 상속을 할 경우 메소드 출처의 모호성 등 여러 가지 문제가 발생할 수 있어 자바에서는 클래스를 통한 다중 상속은 지원하지 않고 인터페이스라는 것을 통해 다중 상속을 지원하고 있다. 즉, 다형성과 관련 있다는 것만 알고있으면 된다. 이제 각 소스 코드를 작성해보자.

 

1. resources>mapper>TestMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/schema/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dbtest.mapper.TestMapper">
    <select id="login" parameterType= "String" resultType="String">
        select password from login where id=#{id};
    </select>
</mapper>

select id를 지정해야만 mapper, service에서 성공적으로 사용할 수 있다. parameterType의 경우 #{id}가 어떤 것으로 들어오는지, resultType은 말 그대로 결과는 어떤 type인지 지정해주는 것이다. 이번에는 진짜 단순하게 비밀번호만 들고오는 것이기 때문에 이렇게 쉽게 String으로 지정했지만, DTO를 만들어 형식을 지정한 경우 저기에는 주소(com.example.dbtest.DTO.~)가 풀로 들어가야 한다.

 

2. com.example.dbtest>mapper>TestMapper

package com.example.dbtest.mapper;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface TestMapper {
    String login(String userid);
}

TestMapper는 xml에 적힌 sql을 호출하기 위한 인터페이스로 select id, parameterType, resultType에 맞추어 다음과 같이 지정한다.

 

3. com.example.dbtest>service>TestService

package com.example.dbtest.service;

public interface TestService {
    public String login(String userid);
}

 

com.example.dbtest>service>TestServiceImpl

import com.example.dbtest.mapper.TestMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class TestServiceImpl implements TestService{
    private final TestMapper testMapper;
    @Override
    public String login(String userid){
        return testMapper.login(userid);
    }
}

여기서 @RequiredArgsConstructor는 final 필드에 대해 생성자를 만들어주는 lombok의 annotation이다. 보통 인터페이스를 두고 작성하기에 따라서 인터페이스를 작성했다.. ^^

 

4. com.example.dbtest>controller>TestController

package com.example.dbtest.controller;

import com.example.dbtest.service.TestService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@CrossOrigin("*")
@RequiredArgsConstructor
public class TestController {
    private final TestService testservice;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home() {
        return "Hello World!";
    }

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String test(@RequestParam(value = "id") String id, @RequestParam(value = "pwd") String pwd) {
        String db_pwd= testservice.login(id);
        if(db_pwd.equals(pwd))
            return "YES";
        else
            return "NO";
    }
}

@CrossOrigin("*")을 통해 localhost에서도 api를 주고받을 수 있도록 설정하고 TestService 객체를 하나 만들어 위와 같이 작성한다.


전문적인 로그인 페이지를 만들겠다면 절대 이글의 로그인 방식대로 하면 안된다. 이는 단순히 db와 연결 테스트를 하기 위한 hello world 예제 같이 기본이라 생각하면 된다.

이 글을 포스팅하게 된 계기는 진짜진짜 단순한 로그인만 만들어보고 싶었는데... 다른 곳에서는 전문적으로 로그인을 구축하는 방법을 알려줘서 그냥 직접 해봤다^_^