๋ณธ๋ฌธ์œผ๋กœ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๋ฐ˜์‘ํ˜•

Spring boot, Java 17, Mybatis, PostgreSQL ์ธํ…”๋ฆฌ์ œ์ด ํ™˜๊ฒฝ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋œ ๊ธ€ ์ž…๋‹ˆ๋‹ค.

 

Spring boot ํ”„๋กœ์ ํŠธ ์ƒ์„ฑํ•˜๊ธฐ

1. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

File - Project์—์„œ Spring Boot๋ฅผ ์„ ํƒํ•œ ํ›„

Dependencies๋Š” Spring Web, PostgreSQL, Thymleaf, Loombok ๋“ฑ์„ ์„ ํƒํ•ด ์ฃผ๊ณ  Create๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค.

(์–ด์ฐจํ”ผ ๋‚˜์ค‘์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๊ธฐ๋•Œ๋ฌธ์— ํฌ๊ฒŒ ์ค‘์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.)


2. DB์—ฐ๊ฒฐ์— ํ•„์š”ํ•œ ์„ค์ •ํ•˜๊ธฐ

postgres

build.gradle ์ถ”๊ฐ€

runtimeOnly 'org.postgresql:postgresql'

 

properties ์„ค์ •

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://์ฃผ์†Œ:ํฌํŠธ๋ฒˆํ˜ธ/DB์ด๋ฆ„
spring.datasource.username=postgres
spring.datasource.password=๋น„๋ฐ€๋ฒˆํ˜ธ

 

์ด๋ ‡๊ฒŒํ•˜๋ฉด DB์—ฐ๊ฒฐ์€ ์ •์ƒ์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


mybatis

build.gradle ์ถ”๊ฐ€

 implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.4'

 

properties ์„ค์ •

mybatis.mapper-locations=classpath:mapper/*.xml

 

Application ํด๋ž˜์Šค์— MapperScan ์ถ”๊ฐ€

@SpringBootApplication
@MapperScan("๊ธฐ๋ณธ๊ฒฝ๋กœ.**.mapper")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

 

Mapper ํŒŒ์ผ์— @Mapper ์–ด๋…ธํ…Œ์ด์…˜ ๋ถ™์ด๊ธฐ

@Mapper
public interface MemberMapper {

3. ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๊ธฐ

ํ…Œ์ด๋ธ” ์ƒ์„ฑ

๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„  ํšŒ์›ํ…Œ์ด๋ธ”์„ ๋จผ์ € ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

dbeaver script๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์Šคํ‚ค๋งˆ์— ํ…Œ์ด๋ธ”์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

create table weeks.users (
	user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY key,
	id varchar(30) not null,
	pw varchar(100) not null,
	name varchar(20) not null,
	phone varchar(15)
);

 

DTO ์ƒ์„ฑ

Login๊ณผ ๋”ฐ๋กœ DTO๋ฅผ ๋‚˜๋ˆด๋Š”๋ฐ ๊ท€์ฐฎ์œผ์‹œ๋ฉด MemberDTO ํ•˜๋‚˜๋งŒ ์จ๋„ ๋ฉ๋‹ˆ๋‹ค.

 

LoginDTO

public class LoginDTO {
    private String id;
    private String pw;
}

 

MemberDTO

public class MemberDTO {
    @NotBlank(message = "์•„์ด๋””๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.")
    private String id;
    @NotBlank(message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.")
    private String pw;
    @NotBlank(message = "์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.")
    private String name;
    private String phone;
}

 

@Valid๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์œ ํšจ์„ฑ์„ ๋„ฃ๊ณ ์žˆ์–ด์„œ @NoBlank๋ผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์–ด์žˆ์Šต๋‹ˆ๋‹ค.

๋ธ”๋กœ๊ทธ ๋‚ด์—์„œ ๊ด€๋ จ ๊ฒŒ์‹œ๊ธ€์ด ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

enum

public enum LoginResult {
    SUCCESS,
    NO_ID,
    WRONG_PASSWORD
}

Mapper ์ƒ์„ฑ

์ธํ„ฐํŽ˜์ด์Šค

@Mapper
public interface MemberMapper {
    // ํšŒ์› ํ™•์ธ
    LoginDTO selectUserById(String id);    
}

 

xml

org.example.weeks๋Š” ๋ณธ์ธ ํ”„๋กœ์ ํŠธ ๊ฒฝ๋กœ๋Œ€๋กœ ๋ฐ”๊ฟ”์•ผ ํ•ฉ๋‹ˆ๋‹ค.

<?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="org.example.weeks.member.mapper.MemberMapper">
    <!--ํšŒ์› ํ™•์ธ-->
    <select id="selectUserById" parameterType="string" resultType="org.example.weeks.member.dto.LoginDTO">
        select
            id,
            pw
        from weeks.users
        where id= #{id}
    </select>
</mapper>

 


Service ์ƒ์„ฑ

์ธํ„ฐํŽ˜์ด์Šค

public interface MemberService {
    // ํšŒ์› ํ™•์ธ
    LoginResult login(LoginDTO dto);
}

 

์„œ๋น„์Šค์ž„ํ”Œ

@Service
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {
    private final MemberMapper memberMapper;

    // ํšŒ์› ํ™•์ธ
    @Override
    public LoginResult login(LoginDTO dto) {
        LoginDTO member = memberMapper.selectUserById(dto.getId());
        if (member == null){
            return LoginResult.NO_ID;
        }
        if (member != null && member.getPw().equals(dto.getPw())) {
            return LoginResult.SUCCESS;
        }
        return LoginResult.WRONG_PASSWORD;
    }
}

Controller ์ƒ์„ฑ

@Controller
@RequestMapping("/member")
@RequiredArgsConstructor
public class MemberController {
    private final MemberService memberService;

    @GetMapping("/login")
    public String login() {
        return "member/login";
    }
    
    @PostMapping("/login")
    public String login(LoginDTO dto, Model model, HttpSession session) {
        LoginResult result = memberService.login(dto);
        switch (result){
            case NO_ID:
                model.addAttribute("msg", "๋“ฑ๋ก๋œ ์•„์ด๋””๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.");
                return "member/login";
            case WRONG_PASSWORD:
                model.addAttribute("msg", "๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
                return "member/login";
            case SUCCESS:
                session.setAttribute("loginId", dto.getId());
                return "redirect:/";
        }
        return "member/login";
    }
}

HTML ์ƒ์„ฑ

ํƒ€์ž„๋ฆฌํ”„

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Login</title>
</head>
<body>
    <h2>Login</h2>
    <p th:if="${msg}" th:text="${msg}" style="color:red;"></p>
    
    <form action="#" th:action="@{/member/login}" method="post">
        <input type="text" name="id" placeholder="์•„์ด๋””">
        <input type="password" name="pw" placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ">
        <button type="submit">๋กœ๊ทธ์ธ</button>
    </form>

    <a th:href="@{/member/register}">ํšŒ์›๊ฐ€์ž…</a>
</body>
</html>

 


๊ฒฐ๊ณผ ํ™”๋ฉด

 

์ด ๋‹ค์Œ์œผ๋กœ๋Š” ๋กœ๊ทธ์•„์›ƒ, ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ์„ ์ ์–ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•