Spring Boot 회원가입

열화상카메라·2022년 6월 26일
0

스프링부트 실습

목록 보기
1/1

스프링은 많은 회사들의 상용 웹 시스템을 구동하는데 보편적으로 많이 사용되는 Java 기반의 웹 프레임워크이다. 많은 회사들에서 사용하는 만큼, 기능도 다양하고 기능 별 깊이가 깊다. 그만큼 러닝커브가 높은 편이라, 간단한 기능들을 직접 샘플로 구현해보면서 해당 기능에 대한 이해도를 높이는 방식이 학습 효율이 좋다고 느껴진다.

기능들 중 기초적으로 많이 쓰이는 기능이 회원가입과 로그인이다. 설계에 따라 다양한 방식으로 구현될 수 있겠지만, 최대한 간단한 예제를 찾아 따라해보면서 부족한 부분들을 채워가는 방식으로 포스팅을 작성했다.

회원가입 데이터를 보관하는 데이터베이스 설치 및 세팅부터, 간단한 Form을 이용한 회원가입까지 구현해본다.

참고자료

데이터베이스 세팅

데이터베이스는 친숙한 MySQL 커뮤니티 버전을 사용한다.

  • 개발환경 OS: Windows-11 64bit

MySQL 설치

아래 링크를 통해 별 무리없이 무난하게 데이터베이스 설치가 가능하다.
https://dev.mysql.com/downloads/windows/installer/8.0.html

설치를 진행하게 되면, 아래와 같이 다양한 제품(product)들이 설치되는 것을 볼 수 있는데, 이중 MySQL ServerMySQL Workbench는 반드시 설치하도록 하자.
(설치할 수 있는 제품은 모두 설치하는 것을 권장드린다. 나는 향후 혹시 모를 귀찮음을 해소해주는 방법이라고 생각해서 모두 설치했다.)

테이블 생성

TB_USER 라는 테이블을 생성하는 쿼리이다.
MySQL 설치 후, MySQL Workbench로 접속한 다음, 임의의 schema를 생성하고 아래 SQL을 실행하면 테이블 생성이 완료된다.

CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '사용자번호',
  `member_id` varchar(255) NOT NULL COMMENT '아이디',
  `member_pwd` varchar(256) DEFAULT NULL COMMENT '비밀번호',
  `m_name` varchar(255) NOT NULL COMMENT '사용자명',
  `m_grade_str` varchar(255) NOT NULL COMMENT '권한',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

프로젝트 생성

이전 데이터베이스 세팅을 했다면, 이제 본격적으로 스프링 프로젝트를 생성해볼 차례이다. 스프링 프로젝트 생성을 위해 start.spring.io에 접속하여 아래와 같이 설정한다.

스프링 스켈레톤 코드 생성

  • Project: Gradle Project 선택
  • Language: Java 선택
  • Spring Boot: 2.6.7 선택
    (이 버전이 없다면, 우측에 괄호 표기가 없는 아무 버전이나 선택해도 된다.)

의존 패키지 정보

start.spring.io의 Dependencies에 아래 패키지들을 추가한다.

  • Spring Web WEB
  • Lombok DEVELOPER TOOLS
  • MyBatis Framework SQL
  • MySQL Driver SQL
  • Thymeleaf TEMPLATE ENGINES
  • Spring Security SECURITY

스켈레톤 코드 build.gradle 파일

plugins {
	id 'org.springframework.boot' version '2.7.0'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'jtlim'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
	implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
	implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'mysql:mysql-connector-java'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

DB 연결 설정

  • DB 연결 설정 정보가 있어야만 스프링 어플리케이션 실행이 가능하다.
  • main/resources/application.properties 파일에 아래 내용을 추가한다.
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/[NAME_OF_SCHEMA]
spring.datasource.username=[DB_USERNAME]
spring.datasource.password=[DB_PASSWORD]

Config 생성 - Spring Security

  • 회원가입, 로그인 등의 기능을 만들기 위해서 가장 먼저 해야 할 일은 임의의 request에 대하여 아래 설정을 해야 한다.(Spring Security)
    • 모든 사용자에 대하여 회원가입 페이지에 접근 허용
    • 모든 사용자에 대하여 로그인 페이지 접근 허용
    • 그외 모든 페이지에 대하여 로그인된 사용자만 접근 허용
package jtlim.simplesite.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SpringSecurity {

    @Bean
    public WebSecurityCustomizer assetCustomizer() {
        return (web -> web.ignoring().antMatchers("/css/**", "/script/**", "image/**", "/fonts/**", "lib/**"));
    }

    @Bean
    public SecurityFilterChain baseChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // 로그인 권한은 누구나, resources파일도 모든권한
                .antMatchers("/login", "/logout", "/register", "/access_denied", "/resources/**").permitAll()
                // "/" 도메인 접근 허용
                .antMatchers("/").authenticated()
                .and()
                .formLogin()
                // 로그인 url 설정
                .loginPage("/login")
                // 로그인 처리 로직 url 설정
                .loginProcessingUrl("/login_proc")
                // 로그인 성공시 리다이렉트 url 설정
                .defaultSuccessUrl("/")
                // 로그인 실패시 리다이렉트 url 설정
                .failureUrl("/access_denied") // 인증에 실패했을 때 보여주는 화면 url, 로그인 form으로 파라미터값 error=true로 보낸다.
                .and()
                .csrf().disable();        //로그인 창
        return http.build();
    }
}

정상동작 확인

  • Application 실행 후, 브라우저에 localhost:8080/ 으로 접근 시 localhost:8080/login으로 리다이렉트 되는 것이 확인 되면 Spring Security Config가 정상적으로 설정되었다고 판단할 수 있다.
  • 이때, 아직 /login에 대한 어떠한 준비도 하지 않은 상태이므로, 당연히Whitelabel Error Page 가 표기된다. url 정상 리다이렉트만 확인하고 넘어간다.

회원가입 Controller 생성 - Register

  • MemberController 파일을 생성하고, 아래와 같이 작성한다.
  • "/" GET 요청에 대하여 index 페이지를 응답하고,
  • "/register" GET 요청에 대하여 register 페이지를 응답한다.
package jtlim.simplesite.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
@RequiredArgsConstructor
public class MemberController {
    private final MemberService memberService;

    @GetMapping("/")
    public String root() {
        return "index";
    }

    /**
     * 회원가입 폼(Get 요청)
     */
    @GetMapping("/register")
    public String registerForm() {
        return "register";
    }

    /**
     * 회원가입 폼 제출(Post 요청)
     */
    @PostMapping("/register")
    public String register(Member member) {
        memberService.joinMember(member);
        return "redirect:/register";
    }
}

회원가입 Service 생성

package jtlim.simplesite.service;

import jtlim.simplesite.domain.Member;
import jtlim.simplesite.mapper.MemberMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.Date;

@Service
@RequiredArgsConstructor
public class MemberService implements UserDetailsService {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:sss");
    Date time = new Date();
    String localTime = format.format(time);

    private final MemberMapper memberMapper;

    @Transactional
    public void joinMember(Member member) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        member.setMemberPwd(passwordEncoder.encode(member.getPassword()));
        member.setMGradeStr("ROLE_USER");
        memberMapper.save(member);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return null;
    }
}

회원가입 Template 생성 - Register

  • main/resources/templates/register.html 파일을 생성한다.
<!--회원가입 페이지-->
<!--register.html-->
<!DOCTYPE html>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
  <meta charset="UTF-8">
</head>
<body>
<form method="post" action="/register">
  <div class="container">
    <h1>회원가입</h1>
    <div class="form-group">
      <label for="inputEmail4">아이디</label>
      <input type="text" class="form-control" id="inputEmail4" name="memberId" placeholder="사용자 아이디">
    </div>
    <div class="form-group">
      <label for="inputAddress">이름</label>
      <input type="text" class="form-control" id="inputAddress" name="mName" placeholder="사용자 이름">
    </div>
    <div class="form-group">
      <label for="inputPassword4">비밀번호</label>
      <input type="password" class="form-control" id="inputPassword4" name="memberPwd" placeholder="사용자 비밀번호">
    </div>
    <button type="submit" class="btn btn-primary">가입 완료</button>
  </div>
</form>
</body>
</html>

정상동작 확인

  • 여기까지 Spring Security, Controller, Template 작성이 완료되었다면 회원가입 화면이 잘 뜨는지 확인해보자
  • localhost:8080/register 로 접근하면 아래와 같이 표기되어야 한다.
  • 기능은 하나도 동작 안하고 템플릿만 있을 뿐인데 뭔가 그럴싸하다.
    역시 보이는게 있어야 제맛

Domain 작성 - Member

  • 이제부터는, 회원가입 요청에 따라 유저를 DB에 insert 하고자 한다.
  • 그 첫 단계로, register 화면에서 회원의 정보를 Form 형태로 전달받아 Java 객체를 생성하는 Domain을 작성한다.
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;

// @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode 어노테이션을 한꺼번에 설정해주는 어노테이션
@Data
public class Member implements UserDetails {
    private String memberId; // 로그인에 사용하는 id
    private String memberPwd; // 로그인에 사용하는 비밀번호
    private String mName; // 사용자 닉네임
    private String mGradeStr; // 사용자 권한(향후 권한 관리에서 사용한다. 지금은 사용하지 않으므로 필드만 삽입해두고 넘어가면 되겠다.)

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.singletonList(new SimpleGrantedAuthority(this.mGradeStr));
    }

    @Override
    public String getUsername() {
        return this.memberId;
    }

    @Override
    public String getPassword() {
        return this.memberPwd;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

Mapper 작성 - MemberMapper

  • Mapper는 Form으로부터 Domain 형태로 생성된 사용자에 대한 정보를 SQL로 변환하여 데이터베이스에 기록하는 역할을 한다.
  • Mapper를 사용하지 않으면, 만약 Member가 100개 속성을 가지고 있는 경우 모든 100개 속성에 대하여 하드코딩으로 SQL문을 작성해야 하는 불편함이 발생한다.
package gurutech.dbmatching.mapper;

import gurutech.dbmatching.domain.Member;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface MemberMapper {
    void save(Member member);
}

SQL 작성 - member.xml

  • 실제로 실행될 SQL문을 작성한다.
<?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="jtlim.simplesite.mapper.MemberMapper">
    <!-- 회원가입 -->
    <insert id="save" parameterType="jtlim.simplesite.domain.Member">
        INSERT INTO tb_user
            (member_id, member_pwd, m_name, m_grade_str)
        VALUES(#{memberId},#{memberPwd}, #{mName}, #{mGradeStr});
    </insert>
</mapper>

Mybatis 연동 설정 추가

  • application.properties 파일에 앞서 작성한 query 파일의 위치를 추가해준다.
// 기존 데이터베이스 설정에 사용한 속성들
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/[NAME_OF_SCHEMA]
spring.datasource.username=[DB_USERNAME]
spring.datasource.password=[DB_PASSWORD]

// mybatis 연동
mybatis.mapper-locations=query/*.xml
mybatis.configuration.map-underscore-to-camel-case=true

테스트

  • 아이디, 이름, 비밀번호 모두 cc 라는 단순한 문자열로 입력했다.
  • 데이터베이스 조회 결과, cc라는 member_id를 가진 사용자가 회원가입이 잘 된것을 확인할 수 있다.
profile
안녕하세요 :)

0개의 댓글