Search
Duplicate

ํšŒ์› ๊ฐ€์ž…

ํšŒ์› ๊ฐ€์ž…

Spring Security 6

Code

Preview

1.
๋ฉ”์ธ ํ™”๋ฉด
2.
ํšŒ์› ๊ฐ€์ž…

์ž‘์—… ํ”„๋กœ์„ธ์Šค

1.
ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
2.
ํ”„๋กœ์ ํŠธ ์„ค์ •
3.
ERD
4.
ํ…Œ์ด๋ธ” ์ •์˜
5.
๋„๋ฉ”์ธ
6.
๋ฐ์ดํ„ฐ
7.
์„œ๋น„์Šค
8.
์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •
9.
์š”์ฒญ ๊ฒฝ๋กœ ๋งคํ•‘

Preview

โ€ข
๋ฉ”์ธ ํ™”๋ฉด
โ€ข
ํšŒ์› ๊ฐ€์ž…

๋ฉ”์ธ ํ™”๋ฉด

ํšŒ์› ๊ฐ€์ž…

์ž‘์—… ํ”„๋กœ์„ธ์Šค

1.
ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
โ€ข
build.gradle
โ€ข
spring boot version : 3.x.x
โ€ข
spring security version : 6.x.x
โ€ข
์˜์กด์„ฑ ์„ค์ •
โ—ฆ
Spring Web
โ—ฆ
Spring Boot DevTools
โ—ฆ
Spring Security
โ—ฆ
Lombok
โ—ฆ
Thymeleaf
โ—ฆ
MySQL Driver
โ—ฆ
Mybatis Framework
2.
ํ”„๋กœ์ ํŠธ ์„ค์ •
โ€ข
application.properties
โ—ฆ
๋ฐ์ดํ„ฐ ์†Œ์Šค ์„ค์ •
โ—ฆ
MyBatis ์„ค์ •
4.
ํ…Œ์ด๋ธ” ์ •์˜
โ€ข
USER
โ€ข
USER_AUTH
5.
๋„๋ฉ”์ธ
โ€ข
Users.java
โ€ข
UserAuth.java
6.
๋ฐ์ดํ„ฐ
โ€ข
UserMapper.xml
โ€ข
UserMapper.java
7.
์„œ๋น„์Šค
โ€ข
UserService.java
โ€ข
UserServiceImpl.java
8.
์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •
โ€ข
~/config/SecurityConfig.java
โ€ข
~/config/CommonConfig.java
9.
์š”์ฒญ ๊ฒฝ๋กœ ๋งคํ•‘
โ€ข
~/controller/HomeController.java
โ—ฆ
๋ฉ”์ธ ํ™”๋ฉด
โ–ช
GET
โ–ช
/
โ–ช
index.html
โ—ฆ
ํšŒ์› ๊ฐ€์ž… ํ™”๋ฉด
โ–ช
GET
โ–ช
/join
โ–ช
join.html
โ—ฆ
ํšŒ์› ๊ฐ€์ž… ์ฒ˜๋ฆฌ
โ–ช
POST
โ–ช
/join
โ–ช
/login (๋กœ๊ทธ์ธ์œผ๋กœ ์ด๋™)
โ–ช
/join?error (ํšŒ์›๊ฐ€์ž…์œผ๋กœ ๋‹ค์‹œ ์ด๋™)
โ—ฆ
์•„์ด๋”” ์ค‘๋ณต ํ™•์ธ
โ–ช
POST
โ–ช
/check/{username}
โ–ช
true
โ–ช
false

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

build.gradle

spring boot 3.x.x
spring security 6.x.x
plugins { id 'java' id 'war' id 'org.springframework.boot' version '3.5.9' id 'io.spring.dependency-management' version '1.1.7' } group = 'com.aloha' version = '0.0.1-SNAPSHOT' description = 'Demo project for Spring Boot' java { toolchain { languageVersion = JavaLanguageVersion.of(23) } } 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:3.0.5' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.5' testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { useJUnitPlatform() }
Java
๋ณต์‚ฌ

Spring Boot Version

Language

Group Id

Artifact Id

packaging type

Java version

์˜์กด์„ฑ ์„ค์ •

1.
Spring Web
2.
Spring boot devtools
3.
Lombok
4.
Thymeleaf
5.
Spring Security
6.
MySQL Driver
7.
Mybatis Framework

Spring Web

Spring boot devtools

Lombok

Thymeleaf

Spring Security

MySQL Driver

Mybatis Framework

ํ”„๋กœ์ ํŠธ ์„ค์ •

application.properties

โ€ข
๋ฐ์ดํ„ฐ ์†Œ์Šค ์„ค์ •
โ€ข
MyBatis ์„ค์ •
spring.application.name=security6 # ๋ฐ์ดํ„ฐ ์†Œ์Šค - MySQL spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/aloha?serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&useSSL=false&autoReconnection=true&autoReconnection=true spring.datasource.username=aloha spring.datasource.password=123456 # Mybatis ์„ค์ • mybatis.configuration.map-underscore-to-camel-case=true mybatis.type-aliases-package=com.aloha.security6.domain mybatis.mapper-locations=classpath:mybatis/mapper/**/**.xml
Markdown
๋ณต์‚ฌ

ERD

ํ…Œ์ด๋ธ” ์ •์˜

โ€ข
USER
โ€ข
USER_AUTH

USER

-- user : ํšŒ์› DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `no` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ํšŒ์›๋ฒˆํ˜ธ', `id` VARCHAR(64) NOT NULL UNIQUE COMMENT '์•„์ด๋””', `username` VARCHAR(100) NOT NULL COMMENT '์‚ฌ์šฉ์ž๋ช…', `password` VARCHAR(200) NOT NULL COMMENT '๋น„๋ฐ€๋ฒˆํ˜ธ', `name` VARCHAR(100) NOT NULL COMMENT '์ด๋ฆ„', `email` VARCHAR(200) DEFAULT NULL COMMENT '์ด๋ฉ”์ผ', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '์ƒ์„ฑ์ผ์‹œ', `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '์ˆ˜์ •์ผ์‹œ', `enabled` INT DEFAULT 1 COMMENT 'ํ™œ์„ฑํ™”์—ฌ๋ถ€' ) COMMENT='ํšŒ์›';
SQL
๋ณต์‚ฌ

USER_AUTH

-- user_auth : ํšŒ์›๊ถŒํ•œ DROP TABLE IF EXISTS `user_auth`; CREATE TABLE `user_auth` ( `no` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '๊ถŒํ•œ๋ฒˆํ˜ธ', `id` VARCHAR(64) NOT NULL COMMENT '์‚ฌ์šฉ์žID (UK)', `username` VARCHAR(100) NOT NULL COMMENT '์‚ฌ์šฉ์ž๋ช…', `auth` VARCHAR(100) NOT NULL COMMENT '๊ถŒํ•œ (ROLE_USER, ROLE_ADMIN, ...)', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '์ƒ์„ฑ์ผ์‹œ', `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '์ˆ˜์ •์ผ์‹œ' ) COMMENT='ํšŒ์›๊ถŒํ•œ';
SQL
๋ณต์‚ฌ

๋„๋ฉ”์ธ

โ€ข
Users.java
โ€ข
UserAuth.java

Users.java

@Data @Builder @AllArgsConstructor public class Users { private Long no; @Builder.Default private String id = UUID.randomUUID().toString(); private String username; private String password; private String name; private String email; private Date createdAt; private Date updatedAt; private int enabled; private List<UserAuth> authList; public Users() { this.id = UUID.randomUUID().toString(); } }
Java
๋ณต์‚ฌ

UserAuth.java

@Data @Builder @AllArgsConstructor public class UserAuth { private Long no; @Builder.Default private String id = UUID.randomUUID().toString(); private String username; private String auth; private Date createdAt; private Date updatedAt; public UserAuth() { this.id = UUID.randomUUID().toString(); } }
Java
๋ณต์‚ฌ

๋ฐ์ดํ„ฐ

โ€ข
UserMapper.xml
โ€ข
UserMapper.java

UserMapper.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.aloha.security.mapper.UserMapper"> <resultMap id="UserMap" type="Users"> <id property="no" column="no"/> <!-- PK --> <result property="id" column="id"/> <result property="username" column="username"/> <result property="password" column="password"/> <result property="name" column="name"/> <result property="email" column="email"/> <result property="enabled" column="enabled"/> <result property="createdAt" column="created_at"/> <result property="updatedAt" column="updated_at"/> <!-- JOIN ์ฟผ๋ฆฌ๋ฅผ ์งœ๊ณ  AuthMap ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ์‹ --> <!-- <collection property="authList" resultMap="AuthMap"></collection> --> <!-- ๋ณ„๋„ SELECT ๋กœ ์—ฐ๊ฒฐ --> <collection property="authList" ofType="UserAuth" select="selectAuth" column="username"></collection> </resultMap> <resultMap id="AuthMap" type="UserAuth"> <result property="no" column="no" /> <result property="username" column="username" /> <result property="auth" column="auth" /> </resultMap> <!-- ํšŒ์› ๊ฐ€์ž… --> <insert id="join"> INSERT INTO user ( id, username, password, name, email ) VALUES ( #{id}, #{username}, #{password}, #{name}, #{email} ) </insert> <!-- ํšŒ์› ๊ถŒํ•œ ๋“ฑ๋ก --> <insert id="insertAuth"> INSERT INTO user_auth ( id, username, auth ) VALUES ( #{id}, #{username}, #{auth} ) </insert> <!-- ํšŒ์› ์กฐํšŒ --> <!-- <select id="select" resultMap="UserMap"> SELECT u.* ,auth FROM user u LEFT JOIN user_auth auth ON u.username = auth.username WHERE u.username = #{username} </select> --> <select id="select" resultMap="UserMap"> SELECT * FROM user WHERE username = #{username} </select> <select id="selectAuth" resultType="UserAuth"> SELECT * FROM user_auth WHERE username = #{username} </select> </mapper>
XML
๋ณต์‚ฌ

UserMapper.java

@Mapper public interface UserMapper { // ํšŒ์› ์กฐํšŒ public Users select(String id) throws Exception; // ํšŒ์› ๊ฐ€์ž… public int join(Users user) throws Exception; // ํšŒ์› ์ˆ˜์ • public int update(Users user) throws Exception; // ํšŒ์› ๊ถŒํ•œ ๋“ฑ๋ก public int insertAuth(UserAuth userAuth) throws Exception; }
Java
๋ณต์‚ฌ

์„œ๋น„์Šค

โ€ข
UserService.java
โ€ข
UserServiceImpl.java

UserService.java

public interface UserService { // ์กฐํšŒ public Users select(String username) throws Exception; // ํšŒ์› ๊ฐ€์ž… public int join(Users user) throws Exception; // ํšŒ์› ์ˆ˜์ • public int update(Users user) throws Exception; // ํšŒ์› ๊ถŒํ•œ ๋“ฑ๋ก public int insertAuth(UserAuth userAuth) throws Exception; }
Java
๋ณต์‚ฌ

UserServiceImpl.java

@Service @RequiredArgsConstructor public class UserServiceImpl implements UserService { // @Autowired private UserMapper userMapper; // @Autowired private PasswordEncoder passwordEncoder; private final UserMapper userMapper; private final PasswordEncoder passwordEncoder; @Override public Users select(String username) throws Exception { Users user = userMapper.select(username); return user; } @Override @Transactional public int join(Users user) throws Exception { String username = user.getUsername(); String password = user.getPassword(); // 123456 -> ๐Ÿ”’ F123N905123890N3138932N4 (์•”ํ˜ธํ™”) String encodedPassword = passwordEncoder.encode(password); // ๐Ÿ”’ ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” user.setPassword(encodedPassword); // ํšŒ์› ๋“ฑ๋ก int result = userMapper.join(user); if( result > 0 ) { // ํšŒ์› ๊ธฐ๋ณธ ๊ถŒํ•œ ๋“ฑ๋ก UserAuth userAuth = new UserAuth(); userAuth.setUsername(username); userAuth.setAuth("ROLE_USER"); result = userMapper.insertAuth(userAuth); } return result; } @Override public int update(Users user) throws Exception { // ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ ์•”ํ˜ธํ™” ์ฒ˜๋ฆฌ String password = user.getPassword(); if( password != null && !password.isEmpty() ) { String encodedPassword = passwordEncoder.encode(password); // ๐Ÿ”’ ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” user.setPassword(encodedPassword); } int result = userMapper.update(user); return result; } @Override public int insertAuth(UserAuth userAuth) throws Exception { int result = userMapper.insertAuth(userAuth); return result; } }
Java
๋ณต์‚ฌ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •

โ€ข
~/config/SecurityConfig.java
โ€ข
~/config/CommonConfig.java

~/config/SecurityConfig.java

@Configuration @EnableWebSecurity public class SecurityConfig { // ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ • ๋ฉ”์†Œ๋“œ @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { /// โœ… ์ธ๊ฐ€ ์„ค์ • http.authorizeHttpRequests(auth -> auth .requestMatchers("/**").permitAll()); return http.build(); } /** * ๐Ÿƒ ์•”ํ˜ธํ™” ๋ฐฉ์‹ ๋นˆ ๋“ฑ๋ก * @return */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
Java
๋ณต์‚ฌ

์š”์ฒญ ๊ฒฝ๋กœ ๋งคํ•‘

โ€ข
~/controller/HomeController.java
โ—ฆ
๋ฉ”์ธ ํ™”๋ฉด
โ–ช
GET
โ–ช
/
โ–ช
index.html
โ—ฆ
ํšŒ์› ๊ฐ€์ž… ํ™”๋ฉด
โ–ช
GET
โ–ช
/join
โ–ช
join.html
โ—ฆ
ํšŒ์› ๊ฐ€์ž… ์ฒ˜๋ฆฌ
โ–ช
POST
โ–ช
/join
โ–ช
/login (๋กœ๊ทธ์ธ์œผ๋กœ ์ด๋™)
โ–ช
/join?error (ํšŒ์›๊ฐ€์ž…์œผ๋กœ ๋‹ค์‹œ ์ด๋™)
โ—ฆ
์•„์ด๋”” ์ค‘๋ณต ํ™•์ธ
โ–ช
POST
โ–ช
/check/{username}
โ–ช
true
โ–ช
false

~/controller/HomeController.java

@Slf4j @Controller public class HomeController { @Autowired private UserService userService; /** * ๋ฉ”์ธ ํ™”๋ฉด * ๐Ÿ”— [GET] - / * ๐Ÿ“„ index.html * @return */ @GetMapping("") public String home() { log.info(":::::::::: ๋ฉ”์ธ ํ™”๋ฉด ::::::::::"); return "index"; } /** * ํšŒ์› ๊ฐ€์ž… ํ™”๋ฉด * ๐Ÿ”— [GET] - /join * ๐Ÿ“„ join.html * @return */ @GetMapping("/join") public String join() { log.info(":::::::::: ํšŒ์› ๊ฐ€์ž… ํ™”๋ฉด ::::::::::"); return "join"; } /** * ํšŒ์› ๊ฐ€์ž… ์ฒ˜๋ฆฌ * ๐Ÿ”— [POST] - /join * โžก โญ• /login * โŒ /join?error * @param user * @return * @throws Exception */ @PostMapping("/join") public String joinPro(Users user) throws Exception { log.info(":::::::::: ํšŒ์› ๊ฐ€์ž… ์ฒ˜๋ฆฌ ::::::::::"); log.info("user : " + user); int result = userService.join(user); if( result > 0 ) { return "redirect:/login"; } return "redirect:/join?error"; } /** * ์•„์ด๋”” ์ค‘๋ณต ๊ฒ€์‚ฌ * @param username * @return * @throws Exception */ @ResponseBody @GetMapping("/check/{username}") public ResponseEntity<Boolean> userCheck(@PathVariable("username") String username) throws Exception { log.info("์•„์ด๋”” ์ค‘๋ณต ํ™•์ธ : " + username); Users user = userService.select(username); // ์•„์ด๋”” ์ค‘๋ณต if( user != null ) { log.info("์ค‘๋ณต๋œ ์•„์ด๋”” ์ž…๋‹ˆ๋‹ค - " + username); return new ResponseEntity<>(false, HttpStatus.OK); } // ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์•„์ด๋””์ž…๋‹ˆ๋‹ค. log.info("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์•„์ด๋”” ์ž…๋‹ˆ๋‹ค." + username); return new ResponseEntity<>(true, HttpStatus.OK); } }
Java
๋ณต์‚ฌ

index.html

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>OAuth</title> <!-- bootstrap css --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container col-12 col-lg-4"> <div class="px-4 py-5 mt-5 text-center"> <h1 class="display-5 fw-bold text-body-emphasis">๋ฉ”์ธ ํ™”๋ฉด</h1> </div> <!-- ๋น„ ๋กœ๊ทธ์ธ ์‹œ --> <th:block sec:authorize="isAnonymous()"> <div class="d-grid gap-2"> <a href="/login" class="btn btn-lg btn-primary">๋กœ๊ทธ์ธ</a> <a href="/join" class="btn btn-lg btn-success">ํšŒ์›๊ฐ€์ž…</a> </div> </th:block> <!-- ๋กœ๊ทธ์ธ ์‹œ --> <th:block sec:authorize="isAuthenticated()"> <div class="card"> <div class="inner p-4"> <div class="d-flex flex-column align-items-center"> <div class="item my-2"> <img th:src="${user.profile}" alt="ํ”„๋กœํ•„" class="rounded-circle"> </div> <div class="item my-2"> <h3 th:text="${user.name}"></h3> </div> <div class="item my-2"> <h5 th:text="${user.email}"></h5> </div> <div class="item my-2 w-100"> <span sec:authentication="principal">์ธ์ฆ๋œ ์‚ฌ์šฉ์ž</span> </div> </div> </div> </div> <form action="/logout" method="post"> <!-- CSRF TOKEN --> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"> <div class="d-grid gap-2"> <button type="submit" class="btn btn-lg btn-primary">๋กœ๊ทธ์•„์›ƒ</button> </div> </form> </th:block> </div> <!-- bootstrap js --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
HTML
๋ณต์‚ฌ

join.html

์•„์ด๋”” ์ค‘๋ณต ๊ฒ€์‚ฌ : ๋น„๋™๊ธฐ ์š”์ฒญ์œผ๋กœ ์ด๋ฏธ ๊ฐ€์ž…๋œ ์•„์ด๋””๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ธฐ๋Šฅ
๋น„๋™๊ธฐ ์š”์ฒญ์€ JavaScript ๋กœ XMLHttpRequest, fetch, axios ๋“ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋น„๋™๊ธฐ ๊ด€๋ จ ๋‚ด์šฉ์€ ์•„๋ž˜ ํŽ˜์ด์ง€๋ฅผ ์ฐธ์กฐํ•ด๋ณด์„ธ์š”~!
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ํšŒ์›๊ฐ€์ž…</title> <!-- bootstrap css --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container col-12 col-lg-4"> <div class="px-4 py-5 mt-5 text-center"> <h1 class="display-5 fw-bold text-body-emphasis">ํšŒ์› ๊ฐ€์ž…</h1> </div> <!-- ํšŒ์›๊ฐ€์ž… ์˜์—ญ --> <main class="form-signin login-box w-100 m-auto"> <form id="form" action="/join" method="post" class="needs-validation" onsubmit="return checkSubmit(event)"> <!-- CSRF TOKEN --> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"> <div class="input-group my-2"> <div class="form-floating" id="box-id"> <input type="text" class="form-control" id="username" name="username" value="" placeholder="์•„์ด๋””" autofocus> <label for="username">์•„์ด๋””</label> </div> <div class="input-group-append"> <button type="button" class="btn btn-lg btn-outline-secondary h-100" onclick="checkId()" >์ค‘๋ณตํ™•์ธ</button> </div> </div> <div class="form-floating my-2"> <input type="password" class="form-control" id="password" name="password" placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ"> <label for="password">๋น„๋ฐ€๋ฒˆํ˜ธ</label> </div> <div class="form-floating my-2"> <input type="password" class="form-control" id="passwordCheck" name="passwordCheck" placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ"> <label for="passwordCheck">๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ</label> </div> <div class="form-floating my-2"> <input type="text" class="form-control" id="name" name="name" value="" placeholder="์ด๋ฆ„" autofocus=""> <label for="name">์ด๋ฆ„</label> </div> <div class="form-floating my-2"> <input type="text" class="form-control" id="email" name="email" value="" placeholder="์ด๋ฉ”์ผ" autofocus=""> <label for="email">์ด๋ฉ”์ผ</label> </div> <div class="d-grid gap-2"> <button class="btn btn-lg btn-primary w-100 py-2" type="submit">ํšŒ์›๊ฐ€์ž…</button> <a href="/" class="btn btn-lg btn-success w-100 py-2">๋ฉ”์ธ</a> </div> </form> </main> </div> <!-- bootstrap js --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"></script> <script> // ๐Ÿ’ CRSF TOKEN const csrfToken = "[[${_csrf.token}]]" /* ์•„์ด๋”” ์ค‘๋ณต ํ™•์ธ */ async function checkId() { const username = document.getElementById("username").value; // null ๋˜๋Š” undefined if (!username) { alert("์•„์ด๋””๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”"); return; } try { // ์•„์ด๋”” ์ค‘๋ณต ํ™•์ธ const response = await fetch(`/check/${username}`, { method: 'GET', headers: { 'X-CSRF-TOKEN': csrfToken } }); if (response.ok) { const result = await response.text(); let boxId = document.getElementById('box-id'); if (result === 'true') { alert('์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์•„์ด๋””์ž…๋‹ˆ๋‹ค.'); boxId.classList.remove('needs-validation'); boxId.classList.add('was-validated'); return true; } else { alert('์ค‘๋ณต๋œ ์•„์ด๋””์ž…๋‹ˆ๋‹ค.'); boxId.classList.remove('was-validated'); boxId.classList.add('needs-validation'); } return false; } else { alert('์•„์ด๋”” ์ค‘๋ณต ํ™•์ธ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.'); return false; } } catch (error) { console.error('Error:', error); alert('์•„์ด๋”” ์ค‘๋ณต ํ™•์ธ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.'); return false; } } /* ์ œ์ถœ ํ™•์ธ - ์•„์ด๋”” ์ค‘๋ณต ์ฒดํฌ */ async function checkSubmit(event) { event.preventDefault(); // ํผ ์ œ์ถœ ๋ฐฉ์ง€ // ์•„์ด๋”” ์ค‘๋ณต ์ฒดํฌ const isIdAvailable = await checkId(); if (!isIdAvailable) { return; } document.getElementById("form").submit(); } </script> </body> </html>
HTML
๋ณต์‚ฌ

๋‹ค์Œ ์ฃผ์ œ