๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป ํ”„๋กœ์ ํŠธ/๐Ÿ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๐Ÿ 

[SpringBoot, Vue.js] ๋กœ๊ทธ์ธ ํ™”๋ฉด ๋งŒ๋“ค๊ธฐ, passwordEncoder.matches๋ฅผ ํ†ตํ•ด ์•”ํ˜ธํ™”๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋น„๊ตํ•˜๊ธฐ.

by ._.sori 2025. 8. 31.

 

 

ํšŒ์›๊ฐ€์ž…๊นŒ์ง€ ๋งŒ๋“ค์—ˆ๋‹ค.
์ด์ œ ํšŒ์›๊ฐ€์ž…ํ•œ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ
๋กœ๊ทธ์ธ์„ ํ•ด๋ณด์ž!

 

 

 

 

[ ํšŒ์›๊ฐ€์ž… 1ํŽธ + 2ํŽธ ]

 

[SpringBoot] (IntelliJ, vue.js, H2) ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ 1ํŽธ : dto ๋งŒ๋“ค๊ณ  ์ด๋ฆ„, ํŒจ์Šค์›Œ๋“œ, ์ „ํ™”๋ฒˆํ˜ธ ํŒจ

๋Œ€๋ถ€๋ถ„์˜ ์›น์‚ฌ์ดํŠธ๋Š”ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„๋‹ค.๊ทธ ์ค‘ ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด๋ณด์ž! 1. ํ™”๋ฉด ๋™์ž‘๊ณผ ์ „์ฒด์ฝ”๋“œํšŒ์›๊ฐ€์ž… ์˜์ƒ ํšŒ์›๊ฐ€์ž… ์ค‘๋ณต ๋””๋ ‰ํ† ๋ฆฌ์™€ ํด๋ž˜์Šค Vue์™€ Spring์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” dto

post-this.tistory.com

 

[SpringBoot] (IntelliJ, vue.js, H2) ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ 2ํŽธ : ์•„์ด๋””, ์ด๋ฉ”์ผ ์ค‘๋ณต์ฒดํฌ ๋งŒ๋“ค๊ธฐ.

์ด๋ฆ„, ๋น„๋ฐ€๋ฒˆํ˜ธ, ์ „ํ™”๋ฒˆํ˜ธ๋ฅผํŒจํ„ด ์ฒดํฌ๋ฅผ ํ–ˆ์—ˆ๋‹ค.์ด๋ฒˆ์—๋Š” ์•„์ด๋””์™€ ์ด๋ฉ”์ผ์„์ค‘๋ณต ์ฒดํฌ ํ•ด๋ณด์ž! [ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ 1ํŽธ : ์ด๋ฆ„, ํŒจ์Šค์›Œ๋“œ, ์ „ํ™”๋ฒˆํ˜ธ ํŒจํ„ด ์ฒดํฌ ]์ด๊ณณ์— ๋“ค์–ด๊ฐ€์‹œ๋ฉด ํŽ˜์ด์ง€

post-this.tistory.com

 

 


 

 

 

1. ํ™”๋ฉด ๋™์ž‘๊ณผ ์ „์ฒด ์ฝ”๋“œ

  • ํ™”๋ฉด ๋™์ž‘

 

 

 

  • LoginView ์ฝ”๋“œ(vue)
 

Register-Web-frontend/src/views/LoginView.vue at master · hyeong-ing/Register-Web-frontend

์ผ๋ฐ˜ ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ, ๊ฐ„ํŽธ ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. WebStrom์œผ๋กœ Vue.js์™€ Vite๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. - hyeong-ing/Register-Web-frontend

github.com

 

 

  • CustomerRight ์ฝ”๋“œ(vue)
 

Register-Web-frontend/src/components/customer/CustomerRight.vue at master · hyeong-ing/Register-Web-frontend

์ผ๋ฐ˜ ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ, ๊ฐ„ํŽธ ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. WebStrom์œผ๋กœ Vue.js์™€ Vite๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. - hyeong-ing/Register-Web-frontend

github.com

 

 

  • login ๋””๋ ‰ํ„ฐ๋ฆฌ(spring)
 

Register-Web-backend/src/main/java/com/example/JoinWeb/login at master · hyeong-ing/Register-Web-backend

์ผ๋ฐ˜ ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ, ๊ฐ„ํŽธ ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. Contribute to hyeong-ing/Register-Web-backend development by creating an account on GitHub.

github.com

 

 

  • ๋””๋ ‰ํ„ฐ๋ฆฌ์™€ ํด๋ž˜์Šค

์™ผ์ชฝ์ด spring, ์˜ค๋ฅธ์ชฝ์ด vue

 

 

 

 


 

 

 

 

 

2. ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค๊นŒ?

  • ๋กœ๊ทธ์ธ ํ™”๋ฉด

 

์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ๊ฐ€ ์ž…๋ ฅ๋˜์ง€ ์•Š์œผ๋ฉด, ๊ทธ์— ๋งž๋Š” alert์ด ์ถœ๋ ฅ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  DB์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•˜๊ณ  ๋งž์œผ๋ฉด ๋กœ๊ทธ์ธ์ด ๋˜๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค.

 

ํšŒ์›๊ฐ€์ž…๋ณด๋‹ค ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๋งŒ๋“œ๋Š” ๊ฒŒ ๋” ๊ฐ„๋‹จํ–ˆ๋‹ค.  ๋‹ค๋งŒ ์ € ๋ฐ‘์— ์žˆ๋Š” ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ์ด ์ฃฝ์„ ๋ง›์ด์—ˆ๋‹ค. ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ ํฌ์ŠคํŒ…์€ ๋‹ค์Œ์—...๐Ÿ˜‚

 

๋กœ๊ทธ์ธ์„ ๋ˆ„๋ฅด๊ณ  ์„ฑ๊ณตํ•˜๋ฉด ๋กœ๊ทธ์ธ์ด ๋˜์—ˆ๋‹ค๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ณ , ์‹คํŒจํ•˜๋ฉด ์•„์ด๋””๋‚˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•ด ๋‹ฌ๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.

 

๋กœ๊ทธ์ธ์„ ์„ฑ๊ณตํ•˜๊ฒŒ ๋˜๋ฉด ํ™”๋ฉด์ด ์ „ํ™˜๋˜๋Š”๋ฐ, ์‚ฌ์šฉ์ž์˜ ์•„์ด๋””๊ฐ€ ํ‘œ์‹œ๋˜๋„๋ก ํ–ˆ๋‹ค.

 

 

 

 

 

  • ๋กœ๊ทธ์ธ ์„ฑ๊ณต ํ™”๋ฉด

 

 

 

 

 


 

 

 

 

3. LoginView.vue

<template> ์ฝ”๋“œ 

<div class="login-container">
      <div class="id-box">
        <p>เท† ID </p>
        <input v-model="userId" @input="userId" type="text" name="userId" placeholder="id"/>
      </div>
      <div class="pw-box">
        <p>เท† PW </p>
        <input v-model="pw" type="password" name="password" placeholder="password"/>
      </div>
        <button class="login-text" @click="login"> SIGN IN </button>
</div>
์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ์นธ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ์ฝ”๋“œ์—์„œ ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ์— v-model์„ ์ •์˜ํ–ˆ๋‹ค. v-model์€ ์–‘๋ฐฉํ–ฅ ๋ฐ”์ธ๋”ฉ์œผ๋กœ ์ž…๋ ฅ์ฐฝ ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๊ณ , ๋ฐ˜๋Œ€๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ํ™”๋ฉด์˜ ์ž…๋ ฅ๊ฐ’๋„ ์ž๋™์œผ๋กœ ๋ฐ”๋€๋‹ค.

 

<script> ์ฝ”๋“œ

  name: 'LoginView',
  data() {
    return {
      userId: "",
      pw: ""
    };
  },
v-model์„ ์“ด ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ๋Š” input๊ณผ ์—ฐ๊ฒฐ๋˜๊ณ  ์ž…๋ ฅ์ฐฝ์— ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด data( ) ๊ฐ’์ด ์ž๋™์œผ๋กœ ๋ฐ”๋€๋‹ค.

 

/* ํ›„์— ์ฝ”๋“œ๊ฐ€ ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.(๋กœ๊ทธ์ธ ์„ฑ๊ณต ํ›„ ํ™”๋ฉด ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋€œ) */

methods: {
    async login() {
    /* ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์ฝ”๋“œ */
      try {
        /* ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ํ•ด๋‹น ๊ฒฝ๋กœ๋กœ ๋ณด๋‚ด๊ธฐ */
        const res = await axios.post("http://localhost:8080/api/login", {
          /* ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ */
          userId: this.userId,
          pw: this.pw
        });
        /* ๋กœ๊ทธ์ธ ์„ฑ๊ณต ๋ฉ”์‹œ์ง€ */
        alert("๋กœ๊ทธ์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
        /* userId๋ฅผ ์ž ์‹œ ์ €์žฅ */
        localStorage.setItem("displayName", this.userId);
        /* ๋กœ๊ทธ์ธ ์„ฑ๊ณตํ•˜๋ฉด path์— ์ ํžŒ ๊ฒฝ๋กœ๋กœ ๋„˜๊น€, query์— ์ ์€ ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ */
        this.$router.push({
          path: "/customer-view",
          query: { userId: this.userId }
        });
      } catch (e) { /* ๋กœ๊ทธ์ธ ์‹คํŒจ ์ฝ”๋“œ */
        alert(e.response.data); /* ์‹คํŒจ ์›์ธ์— ๋งž๋Š” ์˜ค๋ฅ˜๋ฉ”์‹œ์ง€ */
      }
    }
  }
spring์œผ๋กœ ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ๋ฅผ ๋ณด๋‚ด ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋กœ๊ทธ์ธ์ด ๋˜๋„๋ก ๋งŒ๋“ค ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ post๋กœ ํ•ด๋‹น ๊ฒฝ๋กœ์— ๊ฐ€์žฅ ์ตœ์‹ ๊ฐ’์ธ userId์™€ pw๋ฅผ ๋ณด๋ƒˆ๋‹ค. ์ตœ์‹ ๊ฐ’์ด ์˜ฌ ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋Š” v-model์„ ์ผ๊ธฐ ๋•Œ๋ฌธ!

try์—๋Š” ๋กœ๊ทธ์ธ์ด ์„ฑ๊ณต๋˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ์™€ ์„ฑ๊ณต ์ดํ›„์— ๋Œ€ํ•œ ์ฝ”๋“œ๋ฅผ ์ ๋Š”๋‹ค. catch๋Š” ์‹คํŒจํ•œ ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ์ฝ”๋“œ๋ฅผ ์ ๋Š”๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๊ฒŒ ๋˜๋ฉด "๋กœ๊ทธ์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."๊ฐ€ alert ๋˜๊ณ , ๋กœ๊ทธ์ธ์ด ๋˜๋ฉด ๋ณด์—ฌ์ค„ ํŽ˜์ด์ง€๋ฅผ ์ ์—ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ ํ›„์— ์„œ์ˆ ํ•˜๊ฒ ๋‹ค.

๋กœ๊ทธ์ธ์— ์‹คํŒจํ•œ ๊ฒฝ์šฐ ์™œ ์‹คํŒจํ–ˆ๋Š”์ง€์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ๋ฉ”์‹œ์ง€๋ฅผ spring์—๊ฒŒ ๋ฐ›์„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ response.data๋ผ๊ณ  ๋˜์–ด์žˆ๋‹ค. ์‘๋‹ต์œผ๋กœ ์˜จ ๋ฉ”์‹œ์ง€๋ฅผ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅํ•œ๋‹ค.

์ž์„ธํ•œ ์ฝ”๋“œ๋Š” git์— ์žˆ์œผ๋‹ˆ ํ™•์ธํ•ด ๋ณด์„ธ์š”! +) ์ฐธ๊ณ ๋กœ Git์—” ์นด์นด์˜ค ๋กœ๊ทธ์ธ์ด๋ž‘ ๊ฐ™์ด ๋˜์–ด์žˆ์–ด์„œ ์—ฌ๊ธฐ์— ์˜ฌ๋ฆฐ ์ฝ”๋“œ๊นŒ์ง€๊ฐ€ ๋กœ๊ทธ์ธ ๊ด€๋ จ์ด๊ณ  ๊ทธ ๋ฐ‘์— ์žˆ๋Š” ์ฝ”๋“œ๋Š” ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

 

 

 

 


 

 

 

 

4. LoginRequest.class (Spring)

  • DTO: vue์—์„œ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์ด๊ณณ์—์„œ ๋ฐ›์•„๋‘ 
@Getter
@Setter
@ToString(exclude="pw")
public class LoginRequest {
    private String userId;
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String pw;
}
์Šคํ”„๋ง์œผ๋กœ ๋Œ์•„๊ฐ€์„œ ๋กœ๊ทธ์ธ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ด๋‹ค. LoginRequest๋Š” vue๋กœ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š” ํด๋ž˜์Šค๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ํšŒ์›๊ฐ€์ž…์—์„œ ํ•˜์ง€ ์•Š์•˜๋˜ ๋ณด์•ˆ์„ ์•„์ฃผ ์‚ด์ง ์ฒ˜๋ฆฌํ•ด ๋ดค๋‹ค.

๋จผ์ € @ToString(exclude="pw")์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ฒ ๋‹ค. ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ์ด ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๊ฐœ๋ฐœ์ž๋Š” ์ œ๋Œ€๋กœ ๋Œ์•„๊ฐ€๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋กœ๊ทธ๋ฅผ ์ฐ์–ด๋ณผ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ƒฅ @ToString๋งŒ ์„ค์ •ํ•ด ๋‘๋ฉด ๋ชจ๋“  ํ•„๋“œ๊ฐ€ ๋‹ค ์ฐํ˜€ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋…ธ์ถœ๋˜๋Š” ๋ณด์•ˆ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ๋ž˜์„œ exclude๋ฅผ ์ด์šฉํ•ด pw์ธ ํŒจ์Šค์›Œ๋“œ๋Š” ๋ฌธ์ž์—ด์— ํฌํ•จ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ–ˆ๋‹ค. 

@JsonProperty๋Š” Jackson์ด JSON๊ณผ ๊ฐ์ฒด๋กœ(ํ˜น์€ ๊ทธ ๋ฐ˜๋Œ€๋กœ) ๋ณ€ํ™˜ํ•  ๋•Œ pw ํ•„๋“œ๋Š” ์ž…๋ ฅ๋งŒ ํ—ˆ์šฉํ•˜๊ณ  ์ถœ๋ ฅ์€ ํฌํ•จํ•˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ•œ ๊ฒƒ์ด๋‹ค. ๋กœ๊ทธ์ธ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด  vue์—์„œ pw๋ฅผ ๋ฐ›์•„์˜ฌ ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‹ค์ˆ˜๋กœ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ DTO๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด(๋ฐ›์•„์˜จ ๊ฒƒ๊ณผ ๋ฐ˜๋Œ€๋กœ vue๋กœ ์ •๋ณด๋ฅผ ๋ณด๋‚ด๊ฒŒ ๋œ๋‹ค๋ฉด) ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜ ์„ค์ •์œผ๋กœ ์ธํ•ด pw๋Š” ์•Œ์•„์„œ ๋น ์ง€๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

 

 

 


 

 

 

 

5. CheckLogin.class (Spring)

  • ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ null, ๊ณต๋ฐฑ, ๋„์–ด์“ฐ๊ธฐ๋งŒ ์žˆ๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋„์šธ ๋ฉ”์‹œ์ง€
public class CheckLogin {

    public static String checkId(String userId) {
        if(userId == null || userId.isBlank()) {
            return "์•„์ด๋””๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.";
        }
        return "ok";
    }

    public static String checkPw(String pw) {
        if(pw == null || pw.isBlank()) {
            return "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.";
        }
        return "ok";
    }
}
ํšŒ์›๊ฐ€์ž…์—์„œ ์ ์—ˆ๋˜ ์ฝ”๋“œ ์ค‘ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ๊ฐ€์ ธ์™”๋‹ค. ๋กœ๊ทธ์ธ ํ™”๋ฉด์—์„œ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ์ฐฝ์— ๊ฐ’์ด ์ ํ˜€์žˆ์ง€ ์•Š์œผ๋ฉด, ํ•ด๋‹น ๋ฉ”์‹œ์ง€๋ฅผ ๋ฆฌํ„ดํ•˜๋„๋ก ํ–ˆ๊ณ , ๋ฌธ์ œ๊ฐ€ ์—†์œผ๋ฉด ok๊ฐ€ ๋ฆฌํ„ด๋˜๋„๋ก ํ–ˆ๋‹ค.

 

 

 

 


 

 

 

 

6. MemberRepository.interface (Spring)

  • ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋กœ ์•„์ด๋””๋ฅผ ์ฐพ์•„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ณณ
public interface MemberRepository extends JpaRepository<Member, Long> {

    //์ด๋ฉ”์ผ ์•„์ด๋”” ์ค‘๋ณต ์ฒดํฌ -> ํšŒ์›๊ฐ€์ž… ๋ถ€๋ถ„
    boolean existsByUserId(String userId);
    boolean existsByEmail (String email);

    // @@ userId ๋ ˆํฌ์ง€ํ† ๋ฆฌ์—์„œ ์ฐพ๊ธฐ @@
    Optional<Member> findByUserId(String userId);
}
์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ์— ์ž…๋ ฅํ•œ ์•„์ด๋””๋ฅผ ๊ฐ€์ง€๊ณ  ํ•ด๋‹น ์•„์ด๋””๊ฐ€ ์žˆ๋Š”์ง€ ์ฐพ๋Š”๋‹ค. ์—ฌ๊ธฐ์„œ Optional์ด๋ž€ ํƒ€์ž…์„ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ๊ทธ ์ด์œ ๋Š” ์•„์ด๋””๊ฐ€ ์—†์„ ๊ฒฝ์šฐ null ์˜ˆ์™ธ๊ฐ€ ์ƒ๊ธฐ๋Š” ๊ฑธ ๋ฐฉ์ง€ํ•˜๊ณ ์ž ์‚ฌ์šฉํ–ˆ๋‹ค. Optional <T>๋Š” null์ด ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๊ฐ’์„ ๊ฐ์‹ธ๋Š” Wrapper ํด๋ž˜์Šค๋กœ, ์ฐธ์กฐํ•˜๋”๋ผ๋„ NPE(Null Pointer Exception)๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋„์™€์ค€๋‹ค.

<Member>๊ฐ€ Optional์— ๊ฐ™์ด ์ ํ˜€์žˆ๋Š” ์ด์œ ๋Š” extends JpaRepository <Member, Long>์„ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋‹ค. ํšŒ์›๊ฐ€์ž…์„ ๋งŒ๋“ค๋ฉด์„œ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์™€ ์—ฐ๊ฒฐํ•  ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ ๊ทธ ํด๋ž˜์Šค ์ด๋ฆ„์ด Member์˜€๋‹ค. ๊ทธ๋ž˜์„œ Member๋ฅผ ์ ์–ด๋‘” ๊ฒƒ์ด๋‹ค. (Member ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์ €์žฅ, ์กฐํšŒ๋ฅผ ํ•œ๋‹ค.) Optional<Member>๋Š” Member ํƒ€์ž… ๊ฐ’์ด ๋“ค์–ด์žˆ์„ ์ˆ˜๋„ ์žˆ๊ณ  ์•„๋‹ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค.

findByUserId๋Š” Spring Data JPA์˜ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ๊ธฐ๋ฐ˜ ์ฟผ๋ฆฌ๋ผ๋Š” ๊ฒƒ์œผ๋กœ ์—”ํ‹ฐํ‹ฐ ํ•„๋“œ ์ด๋ฆ„์„ ํ•ด์„ํ•ด์„œ JPQL์„ ๋งŒ๋“ค์–ด ์‹คํ–‰ํ•ด ์ค€๋‹ค. ๊ทธ๋ƒฅ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด์„œ ๊ทœ์น™์— ๋”ฐ๋ผ ์ด๋ฆ„์„ ์„ค์ •ํ•˜๋ฉด Spring Data JPA๋ผ๋Š” ์• ๊ฐ€ ๊ทœ์น™์— ๋งž์ถฐ ์ด๋ฆ„์„ ํ•ด์„ํ•ด ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค. ์ง€๊ธˆ์€ findByUserId๋‹ˆ๊นŒ, SELECT m FROM Member m WHERE m.userId = :id ์ด๋Ÿฐ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด์งˆ ๊ฒƒ์ด๋‹ค.(์•„๋งˆ๋„?) ์•„์ฃผ ๊ฐ„ํŽธํ•˜๋‹ค!

 

 

 


 

 

 

7. MemberService.class (Spring)

  • ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค, ํŒจ์Šค์›Œ๋“œ ๋งค์น˜ ํ™•์ธํ•˜๋Š” ๊ณณ
public class MemberService {

    //์˜์กด์„ฑ ์ฃผ์ž…
    @Autowired
    private MemberRepository memberRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;

    //์•„์ด๋””, ์ด๋ฉ”์ผ ์ค‘๋ณต ์ฒดํฌ -> ํšŒ์›๊ฐ€์ž…
    public Boolean existByUserId (String userId) {
        return memberRepository.existsByUserId(userId);
    }

    public Boolean existByEmail (String email) {
        return memberRepository.existsByEmail(email);
    }

    //ํšŒ์›๊ฐ€์ž…์‹œ, ํŒจ์Šค์›Œ๋“œ ์•”ํ˜ธํ™” -> ํšŒ์›๊ฐ€์ž…
    public Member save(Member member) {
        member.setPw(passwordEncoder.encode(member.getPw()));
        return memberRepository.save(member);
    }

    // @@ ๋กœ๊ทธ์ธ ์•„์ด๋”” ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ์•”ํ˜ธํ™”๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋น„๊ตํ•˜๊ธฐ @@    
    public boolean validateMember(LoginRequest loginRequest) {
        return memberRepository.findByUserId(loginRequest.getUserId())
                .map(member -> passwordEncoder.matches(loginRequest.getPw(), member.getPw()))
                .orElse(false);
    }

}
LoginRequest ํ•„๋“œ(์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ)๋ฅผ ๋ชจ์กฐ๋ฆฌ ๋“ค๊ณ  ์™”๋‹ค. ์•„์ด๋””๊ฐ€ ์žˆ๋Š”์ง€, ์žˆ๋‹ค๋ฉด ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋งค์น˜ํ•ด์„œ ๋งž๋Š”์ง€ ํ™•์ธํ•  ๊ฒƒ์ด๋‹ค. ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋งž์œผ๋ฉด true, ํ‹€๋ฆฌ๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•  ๊ฑฐ๋ผ ๋ฐ˜ํ™˜ํƒ€์ž…์€ boolean์ด ๋œ๋‹ค.

memberRepository.findByUserId(loginRequest.getUserId()) ์ด ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด๋ฉด, loginRequest์—์„œ userId ํ•„๋“œ๋ฅผ ๊ฐ€์ ธ์™€ memberRepository์— ์žˆ๋Š” findByUserId ๋ฉ”์„œ๋“œ์— ๋„ฃ์–ด๋ผ ๋ผ๋Š” ๋ง์ด ๋œ๋‹ค.
๊ทธ๋ฆฌ๊ณ  LoginRequest ํ•„๋“œ๋ฅผ ๋“ค๊ณ  ์˜จ ์ด์œ ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ๋ฅผ ๊บผ๋‚ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. findByUserId์—์„œ Optional<Member>๋กœ ๋Œ๋ ค๋ฐ›๋„๋ก ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์žˆ์œผ๋ฉด Optional.of(member)๊ฐ€ ์˜ฌ ํ…Œ๊ณ  ์—†๋‹ค๋ฉด Optional.empty( )๊ฐ€ ์˜ค๊ฒŒ ๋œ๋‹ค.

.map(member -> passwordEncoder.matches(loginRequest.getPw(), member.getPw())) ์ด ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด์ž. ์—ฌ๊ธฐ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋  ๋•Œ๋Š” ๊ฐ’์ด ์žˆ์„ ๊ฒฝ์šฐ๊ฐ€ ๋œ๋‹ค. ๊ฐ’์ด ์žˆ์„ ๋•Œ ์‹คํ–‰๋˜๋Š” ์ด์œ ๋Š” map์— ์žˆ๋‹ค. Optional์—์„œ ์ฒด์ด๋‹์ด๋ž€ ๊ฑด๋ฐ, map. filter, flatMap, orElse ๊ฐ™์€ ์—ฐ์‚ฐ์„ ์—ฐ๊ฒฐํ•ด์„œ ๊ฐ’์ด ์žˆ์„ ๋•Œ ์ˆœ์„œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ์—†์œผ๋ฉด ๋น ์ ธ๋‚˜์˜ค๋Š” ํŒจํ„ด์ด๋‹ค. loginRequest์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์ฐฝ์— ์ž…๋ ฅํ•œ ํŒจ์Šค์›Œ๋“œ๋ฅผ ๋ฐ›์•„์˜ค๊ณ , member๋ฅผ ํ†ตํ•ด ์›๋ž˜ ์ €์žฅ๋˜์–ด ์žˆ๋˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ spring security๋ฅผ ์ด์šฉํ•ด passwordEncoder๋ฅผ ํ•ด์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•”ํ˜ธํ™”์‹œํ‚จ ์ฑ„ ์ €์žฅํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‘˜์ด ๋น„๊ตํ•  ๋•Œ๋„ passwordEncoder.matches๋ฅผ ํ•ด์ค˜์•ผ ํ•œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ํ‰๋ฌธ ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ DB์˜ ํ•ด์‹œ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋น„๊ตํ•ด boolean์„ ๋Œ๋ ค์ค€๋‹ค.

.orElse(false);๋Š” ์•„์ด๋””๊ฐ€ ์—†์„ ๊ฒฝ์šฐ false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋กœ๊ทธ์ธ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋กœ๊ทธ์ธ์„ ์‹œ๋„ํ•˜๊ฒŒ ๋˜๋ฉด MemberService์—์„œ boolean ๊ฐ’์œผ๋กœ ๋ฐ›์•„์˜ค๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

 

 

 


 

 

 

 

8. CheckLogin.class (Spring)

  • ๋กœ๊ทธ์ธ ์ปจํŠธ๋กค๋Ÿฌ, ๋ฐฉ๊ธˆ๊นŒ์ง€ ๋งŒ๋“  ํด๋ž˜์Šค์™€ ์ฝ”๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ ์ด๊ณณ์—์„œ ์ตœ์ข…์ ์œผ๋กœ ์ฒ˜๋ฆฌ ํ›„ vue๋กœ ์‘๋‹ต
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "http://localhost:5173")
public class LoginController {
   
    // ์˜์กด์„ฑ ์ฃผ์ž…
    @Autowired
    private MemberService memberService;

    // login ๊ฒฝ๋กœ๋กœ ์˜ค๋ฉด login ๋ฉ”์„œ๋“œ ์‹คํ–‰
    @PostMapping("/login")
    // ๋ฐ˜ํ™˜ํƒ€์ž…์€ ResponseEntity<String>์ด๊ณ  @RequestBody๋กœ ์š”์ฒญ ๋ฐ›์•„์˜ด
    public ResponseEntity<String> login(@RequestBody LoginRequest loginRequest) {

        //loginRequest์—์„œ id์™€ pw๋ฅผ ๊บผ๋‚ด์„œ checkํ•˜๊ธฐ.
        String idCheckResult = CheckLogin.checkId(loginRequest.getUserId());
        if (!"ok".equals(idCheckResult)) { //ok๊ฐ€ ์•„๋‹ˆ๋ฉด
            //๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ, checkId ๋ฉ”์„œ๋“œ์—์„œ ๋ฐ˜ํ™˜ํ•œ ํ…์ŠคํŠธ๊ฐ€ ์ด๊ณณ์— ๋ฐ”๋””๋กœ ์‹ค๋ฆฐ๋‹ค.
            return ResponseEntity.badRequest().body(idCheckResult);
        }

        String pwCheckResult = CheckLogin.checkPw(loginRequest.getPw());
        if (!"ok".equals(pwCheckResult)) {
            return ResponseEntity.badRequest().body(pwCheckResult);
        }

        //loginRequest์— ์žˆ๋Š” ํ•„๋“œ๋ฅผ validateMember ๋ฉ”์„œ๋“œ๋กœ ๋ณด๋ƒ„
        boolean validated = memberService.validateMember(loginRequest);
        if (!validated) { //validated๊ฐ€ ์•„๋‹ˆ๋ฉด ํ•ด๋‹น ๋ฉ”์‹œ์ง€ ๋ณด๋ƒ„
            return ResponseEntity.badRequest().body("์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ด์ฃผ์„ธ์š”.");
        }
        
        // ๊ดœ์ฐฎ์œผ๋ฉด ok ๋ณด๋ƒ„
        return ResponseEntity.ok("ok");
    }
}
์ƒํƒœ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ๋‚ด์šฉ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด, login๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜ํƒ€์ž…์€ ResponseEntity<String>์ด ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ return ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ResponseEntity๊ฐ€ ๋ถ™๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋’ค์— badRequest ํ˜น์€ ok๊ฐ€ ๋”ฐ๋ผ์˜จ๋‹ค. ์ด badRequest์™€ ok๊ฐ€ ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. ok๋Š” ๋ณดํ†ต 200์„ ์˜๋ฏธํ•˜๊ณ , badRequest๋Š” ๋ณดํ†ต 400์„ ์˜๋ฏธํ•œ๋‹ค. body ๊ด„ํ˜ธ ์•ˆ์— ๋ฌธ๊ตฌ๋ฅผ ์ ์—ˆ๋‹ค. ๊ฐ ์˜ค๋ฅ˜์— ๋งž์ถฐ ์‚ฌ์šฉ์ž๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๊ฒŒ ๋œ๋‹ค. vue์—์„œ ์ ์€ alert(e.response.data); ์ฝ”๋“œ๊ฐ€ ๋ณด์—ฌ์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. 

์‚ฌ์šฉ์ž๊ฐ€ ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์ปจํŠธ๋กค๋Ÿฌ์— ๋„์ฐฉํ•ด์„œ ์ˆœ์„œ๋Œ€๋กœ ํ™•์ธํ•œ๋‹ค. ๋งŒ์•ฝ ์ž…๋ ฅํ•œ ๊ฐ’์ด ์—†๋Š” ์ƒํƒœ์—์„œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด "์•„์ด๋””๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”"์™€ ๊ฐ™์€ ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค. ๋งŒ์•ฝ ๊ฐ’์ด ๋“ค์–ด์™”๋‹ค๋ฉด MemberService ํด๋ž˜์Šค์— ์žˆ๋˜ validateMember ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘๋™์‹œํ‚จ๋‹ค. ์•„๊นŒ validateMember๋Š” ์•„์ด๋””๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ์žˆ์œผ๋ฉด ํŒจ์Šค์›Œ๋“œ๋ฅผ ๋น„๊ตํ•˜์—ฌ ๊ทธ ๊ฐ’์„ boolean์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ boolean validated๋ผ๊ณ  ๋˜์–ด์žˆ๋Š” ์ด์œ ๊ฐ€ ๋ฐ˜ํ™˜๊ฐ’์ด boolean์ด๋ผ์„œ ๊ทธ๋ ‡๋‹ค. ๋ฐ˜ํ™˜๋œ ๊ฐ’(true ํ˜น์€ false)์„ validated์— ๋ณด๊ด€ํ•˜๊ณ  ๊ทธ ๊ฐ’์ด false์ด๋ฉด "์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ด ์ฃผ์„ธ์š”" ๋ฉ”์‹œ์ง€๋ฅผ badRequest ์ƒํƒœ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ๋ณด๋‚ธ๋‹ค. ๊ทธ๋ฆฌ๊ณ  true์ด๋ฉด ok ์ƒํƒœ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ "ok"๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ๋ณด๋‚ด์ง„๋‹ค.

 

 

+) ์ฐธ๊ณ ๋กœ ๋ณด์•ˆ์ƒ ๋ฉ”์‹œ์ง€๋Š” ๋ชจํ˜ธํ•˜๊ฒŒ ์ ๋Š”๊ฒŒ ์ข‹๋‹ค. ์•„์ด๋””, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ค‘ ์–ด๋–ค๊ฒŒ ํ‹€๋ ธ๋Š”์ง€ ์•Œ๋ ค์ฃผ๋ฉด ํ•ด์ปค๊ฐ€ ๊ทธ ๋ถ€๋ถ„๋งŒ ์—ด์‹ฌํžˆ ํ’€๋ฉด ๋˜๋‹ˆ๊นŒ..!
+) ๊ทธ๋ฆฌ๊ณ  ๋กœ๊ทธ์ธ ์‹คํŒจ๋Š” 401 ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ์“ฐ๋Š”๊ฒŒ ๋งž๋‹ค๊ณ  ํ•œ๋‹ค. 401์€ Unauthorized๋ผ๊ณ  ์‚ฌ์šฉ์ž ์ด๋ฆ„, ๋น„๋ฐ€๋ฒˆํ˜ธ, ๋˜๋Š” API ํ‚ค ๋“ฑ ์ž…๋ ฅํ•œ ์ธ์ฆ ์ •๋ณด๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ ‡๋‹ค๊ณ  ํ•œ๋‹ค.

return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
    .body("์•„์ด๋”” ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");

 

 

- ํ•„์š”ํ•œ ๊ฐœ๋…์€ ์ด๊ณณ์—

 

'๐ŸŒท Spring/๊ฐœ๋…' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๊ธ€ ๋ชฉ๋ก

๐Ÿงš๐Ÿป ๐Ÿงธ ๐ŸŽ€ ๐Ÿข

post-this.tistory.com

 

 

 

 

๋กœ๊ทธ์ธ ์„ฑ๊ณต ํ™”๋ฉด์€ ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ...

'๐Ÿ’ป ํ”„๋กœ์ ํŠธ > ๐Ÿ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๐Ÿ ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Vue.js] ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ, localStorage๋ฅผ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์•„์ด๋””๊ฐ€ ๋ฉ”์ธํ™”๋ฉด์— ๋ณด์ด๋„๋ก ์„ค์ •ํ•˜๊ธฐ!  (2) 2025.09.02
[SpringSecurity] Vue์™€ Spring์˜ ์„œ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ ๋ฌธ์ œ ํ•ด๊ฒฐํ•˜๊ธฐ.(CORS ์—๋Ÿฌ ํ•ด๊ฒฐํ•˜๊ธฐ)  (3) 2025.08.30
[Vue.js] ๋ฒ„ํŠผ ๋กœ์ง์ด ์ œ๋Œ€๋กœ ๋™์ž‘์„ ์•ˆํ•˜๋Š” ๊ฒƒ ๊ฐ™์„ ๋•Œ, chunk-VZXQDS5F.js?v=5f04997f:2125 [Vue warn]: Data property "idDuplicate" is already defined in Methods.  (0) 2025.08.20
[SpringBoot] (IntelliJ, vue.js, H2) ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ 3ํŽธ: watch๋กœ ์•„์ด๋””์™€ ์ด๋ฉ”์ผ ๋ณ€๊ฒฝ ์‹œ ๋‹ค์‹œ ์ค‘๋ณต์ฒดํฌํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ.  (4) 2025.08.18
[SpringBoot] (IntelliJ, vue.js, H2) ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ 2ํŽธ : ์•„์ด๋””, ์ด๋ฉ”์ผ ์ค‘๋ณต์ฒดํฌ ๋งŒ๋“ค๊ธฐ.  (11) 2025.08.17