[#31] 토큰 인증 방식

2025. 3. 11. 16:54·LG 유플러스 유레카 SW/Spring

토큰 인증 방식

➡️ 백엔드 서버로부터 받은 JSESSIONID를 저장했어도 다음 요청 시 쿠키를 가지고 가지 못하는 문제 해결 방법

  1. 클라이언트가 로그인 요청을 보냄
  2. 서버가 사용자 정보를 검증하고 토큰을 발급
  3. 클라이언트는 토큰을 저장하고 요청 시 포함하여 전송
  4. 서버는 토큰을 검증하여 사용자 인증 수행

 

쇼핑몰 실습 - 토큰 로그인 수행

로그인 테이블 생성

use ureca;

drop table if exists login;

CREATE TABLE `ureca`.`login` (
  `email` VARCHAR(50) NOT NULL primary key,
  `token` VARCHAR(256) NOT NULL unique,
  `logintime` TIMESTAMP NOT NULL DEFAULT current_timestamp);

해싱/암호화를 통한 토큰 생성

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class OpenCrypt {

	 public static byte[] getSHA256(String source, String salt) {
           byte byteData[]=null;
           try{
               MessageDigest md = MessageDigest.getInstance("SHA-256"); 
               md.update(source.getBytes()); 
               md.update(salt.getBytes()); 
               byteData= md.digest();  
               System.out.println("원문: "+source+ "   SHA-256: "+
                                     byteData.length+","+byteArrayToHex(byteData));
           }catch(NoSuchAlgorithmException e){
               e.printStackTrace(); 
           }
           return byteData;
     }
	 	 
	  public static byte[] generateKey(String algorithm,int keySize) throws NoSuchAlgorithmException {
		 
	       KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);	 
	       keyGenerator.init(keySize);
	       SecretKey key = keyGenerator.generateKey();
	       return key.getEncoded();	 
}	

 public static String aesEncrypt(String msg, byte[] key) throws Exception {
       SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        String iv = "AAAAAAAAAAAAAAAA";
        cipher.init(Cipher.ENCRYPT_MODE, 
        		       skeySpec,
        		       new IvParameterSpec(iv.getBytes()));        
        byte[] encrypted = cipher.doFinal(msg.getBytes());     
        return  byteArrayToHex(encrypted);
 }
	 
public static String aesDecrypt(String msg,byte[] key ) throws Exception {
 	        SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
	        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
	        String iv = "AAAAAAAAAAAAAAAA";
	        cipher.init(Cipher.DECRYPT_MODE, 
	        		       skeySpec,
	        		       new IvParameterSpec(iv.getBytes()));  
	        byte[] encrypted = hexToByteArray(msg);
	        byte[] original = cipher.doFinal(encrypted);  
	        return new String(original); 
}
	 
	 public static byte[] hexToByteArray(String hex) {
		    if (hex == null || hex.length() == 0) {
		        return null;
		    }
		 
		    byte[] ba = new byte[hex.length() / 2];
		    for (int i = 0; i < ba.length; i++) {
		        ba[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
		    }
		    return ba;
		}
		 
		// byte[] to hex
		public static String byteArrayToHex(byte[] ba) {
		    if (ba == null || ba.length == 0) {
		        return null;
		    }
		 
		    StringBuffer sb = new StringBuffer(ba.length * 2);
		    String hexNumber;
		    for (int x = 0; x < ba.length; x++) {
		        hexNumber = "0" + Integer.toHexString(0xff & ba[x]);
		 
		        sb.append(hexNumber.substring(hexNumber.length() - 2));
		    }
		    return sb.toString();
		} 
	 
}

Salt 생성/Email 해싱/DB에 토큰 저장

package com.shop.cafe.service;

import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.shop.cafe.dao.LoginDao;
import com.shop.cafe.dao.MemberDao;
import com.shop.cafe.dto.Login;
import com.shop.cafe.dto.Member;
import com.shop.cafe.util.OpenCrypt;

@Service
public class MemberService {
	@Autowired
	MemberDao memberDao;
	
	@Autowired
	LoginDao loginDao;

	
	public Login tokenLogin(Member m) throws Exception {
		m=memberDao.login(m);
		if(m!=null) {
			String nickname=m.getNickname();
			if(nickname!=null && !nickname.trim().equals("")) {
				//member table에서 email과 pwd가 확인된 상황 즉 login ok
				
				String email=m.getEmail();
				
				//1. salt를 생성한다
				String salt=UUID.randomUUID().toString();
				System.out.println("salt:"+salt);
				//2. email을 hashing 한다
				byte[] originalHash=OpenCrypt.getSHA256(email, salt);
				//3. db에 저장하기 좋은 포맷으로 인코딩한다
				String myToken=OpenCrypt.byteArrayToHex(originalHash);
				System.out.println("myToken : "+myToken);
				
				//4. login table에 token 저장
				Login loginInfo=new Login(email, myToken, nickname, null);
				loginDao.insertToken(loginInfo);
				return loginInfo;
			}
		}
		
		return null;		 
	}
	
	public Member login(Member m) throws Exception {
		return memberDao.login(m);
	}
	
	public void insertMember(Member m) throws Exception{
		memberDao.insertMember(m);
	}
	
	public void updateMember(Member m) throws Exception{
		memberDao.updateMember(m);
	}
	
	public void deleteMember(String email) throws Exception{
		memberDao.deleteMember(email);
	}

	public void logout(String authorization) throws Exception {
		loginDao.deleteToken(authorization);
		
	}

}

Login.java

package com.shop.cafe.dto;

import java.util.Date;

public class Login {
	
	private String email, token, nickname;
	private Date loginTime;
	
	public Login(String email, String token, String nickname, Date loginTime) {
		super();
		this.email = email;
		this.token = token;
		this.nickname = nickname;
		this.loginTime = loginTime;
	}

	public Login() {
		super();
		// TODO Auto-generated constructor stub
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}

	public String getNickname() {
		return nickname;
	}

	public void setNickname(String nickname) {
		this.nickname = nickname;
	}

	public Date getLoginTime() {
		return loginTime;
	}

	public void setLoginTime(Date loginTime) {
		this.loginTime = loginTime;
	}

	@Override
	public String toString() {
		return "Login [email=" + email + ", token=" + token + ", nickname=" + nickname + ", loginTime=" + loginTime
				+ "]";
	}
	
	
	
	

}

LoginDao.java

package com.shop.cafe.dao;
import org.apache.ibatis.annotations.Mapper;
import com.shop.cafe.dto.Login;

@Mapper
public interface LoginDao {	
	public void insertToken(Login login) throws Exception;

	public void deleteToken(String token) throws Exception;
}

login.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="com.shop.cafe.dao.LoginDao">
  <insert id="insertToken" parameterType="Login">
  	insert into login(email, token) values(#{email},#{token})
  </insert>
  
  <delete id="deleteToken" parameterType="String">
  	delete from login where token=#{token}
  </delete>
</mapper>

Response Body에 nickname과 token을 넣어 응답

package com.shop.cafe.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Login;
import com.shop.cafe.dto.Member;
import com.shop.cafe.service.MemberService;

@RestController
@CrossOrigin("http://127.0.0.1:5500/")
public class MemberController {
	
	@Autowired
	MemberService memberService;
	
	@PostMapping("logout")
	public void logout(@RequestHeader String authorization) {
		System.out.println(authorization);
		try {
			memberService.logout(authorization);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@PostMapping("tokenLogin")
	public Map<String,String> tokenLogin(@RequestBody Member m) {
		System.out.println(m);
		
		Map<String,String> responseMap=new HashMap<>();
		
		try {
			Login loginInfo=memberService.tokenLogin(m);
			
			if(loginInfo!=null && loginInfo.getNickname()!=null && loginInfo.getToken()!=null) {
				responseMap.put("nickname", loginInfo.getNickname());
				responseMap.put("Authorization", loginInfo.getToken());
			}else {
				responseMap.put("msg", "다시 로그인 해주세요");
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			responseMap.put("msg", "다시 로그인 해주세요");
		}
		return responseMap;
	}
	
	@PostMapping("login")
	public Map<String,String> login(@RequestBody Member m) {
		System.out.println(m);
		
		Map<String,String> responseMap=new HashMap<>();
		
		try {
			m=memberService.login(m);
			String nickname=m.getNickname();
			if(m!=null && nickname!=null && !nickname.trim().equals("")) {
				responseMap.put("nickname", nickname);
			}else {
				responseMap.put("msg", "다시 로그인 해주세요");
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			responseMap.put("msg", "다시 로그인 해주세요");
		}
		return responseMap;
	}
	
	@PostMapping("insertMember")
	public String insertMember(@RequestBody Member m) {
		System.out.println(m);
		try {
			memberService.insertMember(m);
			return m.getNickname()+"님 가입을 환영합니다";
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return "회원 가입 실패";
		}
	}
	
	@PostMapping("updateMember")
	public String updateMember(@RequestBody Member m) {
		System.out.println(m);
		try {
			memberService.updateMember(m);
			return "ok";
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return "email과 pwd 확인해 주세요";
		}
	}
	
	@PostMapping("deleteMember")
	public String deleteMember(@RequestBody String email) {
		System.out.println(email);
		try {
			memberService.deleteMember(email);
			return "ok";
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return "email과 pwd 확인해 주세요";
		}
	}

}

 

프론트엔드

  • 로그인 ok 시에 nicknmae과 token을 session storage에 저장
  • 전역 axios의 header에 token을 저장해 놓으면 모든 axios 요청 시 해당 token이 전송됨
  • logout 시에 해당 token을 삭제
document.getElementById("signupBtn").addEventListener("click", async ()=>{
  const email=document.getElementById("email").value;
  const pwd=document.getElementById("pwd").value;
  const nickname=document.getElementById("nickname").value;

  const data={email,pwd,nickname};

  const response=await axios.post("http://localhost:8080/insertMember" , data);

  document.getElementById("effetMsg").innerHTML = response.data;
});

// document.getElementById("loginBtn").addEventListener("click", async () => {  
//     const email = document.getElementById("loginEmail").value;
//     const pwd = document.getElementById("loginPwd").value;
//     const data = {  email, pwd };
//     const response=await axios.post("http://localhost:8080/login" , data);
//     console.log(response.data);
//     alert(response.data.nickname+"님 반갑습니다");
//   });

document.getElementById("loginBtn").addEventListener("click", async () => {  
  const email = document.getElementById("loginEmail").value;
  const pwd = document.getElementById("loginPwd").value;
  const data = {  email, pwd };
  const response=await axios.post("http://localhost:8080/tokenLogin" , data);
  
  document.getElementById("loginSpan").innerHTML=`${response.data.nickname}  
  <button class="btn btn-danger btn-sm" id="logoutBtn">Logout</button>`;
  const token = response.data.Authorization;
  console.log('Authorization:', token);
  sessionStorage.setItem('Authorization', token);
  sessionStorage.setItem('nickname', response.data.nickname);
  axios.defaults.headers.common['Authorization'] = token; // Authorization 헤더 설정
  // 모달 닫기
  const modal = bootstrap.Modal.getInstance(document.getElementById("loginModal"));
  loginModal.setAttribute("aria-hidden", "true");
  modal.hide();
});

const Authorization = sessionStorage.getItem("Authorization");
const nickname = sessionStorage.getItem("nickname");
if (Authorization && nickname) {
  axios.defaults.headers.common['Authorization'] = Authorization; // Authorization 헤더 설정
  document.getElementById("loginSpan").innerHTML = `${nickname}  
  <button class="btn btn-danger btn-sm" id="logoutBtn">Logout</button>`;
}

document.getElementById("loginSpan").addEventListener("click", async (event)=>{
  if(event.target.id=='logoutBtn'){       
      await axios.post("http://localhost:8080/logout");
      sessionStorage.removeItem("nickname");
      sessionStorage.removeItem("Authorization");
      axios.defaults.headers.common['Authorization'] = ''; // Authorization 헤더에서 삭제       
      window.location.reload();
  }
});
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Bootstrap 5 Website Example</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
    />
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>

    <style>
      .fakeimg {
        height: 200px;
        background: #aaa;
      }
    </style>
  </head>
  <body>
    <div
      class="px-3 py-1 bg-info text-white text-center d-flex justify-content-between"
    >
      <span id="effetMsg">effect!</span>
      <span id="loginSpan">
        <a
          
          href="#"
          data-bs-toggle="modal"
          data-bs-target="#loginModal"
        >
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            stroke-width="1.5"
            stroke="currentColor"
            class="size-6"
            width="24"
            height="24"
          >
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15M12 9l-3 3m0 0 3 3m-3-3h12.75"
            />
          </svg>
        </a>
      </span>
    </div>

    <nav class="navbar navbar-expand-sm bg-dark navbar-dark">
      <div class="container-fluid">
        <ul class="navbar-nav">
          <li class="nav-item" id="signupLi">
            <a
              class="nav-link active"
              href="#"
              data-bs-toggle="modal"
              data-bs-target="#signupModal"
              >SignUp</a
            >
          </li>
          <li class="nav-item"><a class="nav-link" href="#">Link</a></li>
          <li class="nav-item"><a class="nav-link" href="#">Link</a></li>
          <li class="nav-item">
            <a class="nav-link disabled" href="#">Disabled</a>
          </li>
        </ul>
      </div>
    </nav>

    <div class="container mt-5">
      <div class="row" id="productListDiv"></div>
    </div>

    <div class="mt-5 p-4 bg-dark text-white text-center">
      <p>Footer</p>
    </div>

    <!-- signupModal -->
    <div class="modal" id="signupModal">
      <div class="modal-dialog">
        <div class="modal-content">
          <!-- Modal Header -->
          <div class="modal-header">
            <h4 class="modal-title">회원가입</h4>
            <button
              type="button"
              class="btn-close"
              data-bs-dismiss="modal"
            ></button>
          </div>

          <!-- Modal body -->
          <div class="modal-body">
            <div class="mb-3 mt-3">
              <label for="nickname" class="form-label">Nickname:</label>
              <input
                type="text"
                class="form-control"
                id="nickname"
                placeholder="Enter nickname"
                name="nickname"
              />
            </div>
            <div class="mb-3">
              <label for="email" class="form-label">Email:</label>
              <input
                type="email"
                class="form-control"
                id="email"
                placeholder="Enter email"
                name="email"
              />
            </div>
            <div class="mb-3">
              <label for="pwd" class="form-label">Password:</label>
              <input
                type="password"
                class="form-control"
                id="pwd"
                placeholder="Enter password"
                name="pswd"
              />
            </div>

            <button type="submit" class="btn btn-primary" id="signupBtn">
              가입
            </button>
          </div>

          <!-- Modal footer -->
          <div class="modal-footer">
            <button
              type="button"
              class="btn btn-danger"
              data-bs-dismiss="modal"
            >
              Close
            </button>
          </div>
        </div>
      </div>
    </div>

	<!-- loginModal -->
    <div class="modal" id="loginModal">
		<div class="modal-dialog">
		  <div class="modal-content">
			<!-- Modal Header -->
			<div class="modal-header">
			  <h4 class="modal-title">로그인</h4>
			  <button
				type="button"
				class="btn-close"
				data-bs-dismiss="modal"
			  ></button>
			</div>
  
			<!-- Modal body -->
			<div class="modal-body">			  
			  <div class="mb-3">
				<label for="email" class="form-label">Email:</label>
				<input
				  type="email"
				  class="form-control"
				  id="loginEmail"
				  placeholder="Enter email"
				  name="email"
				/>
			  </div>
			  <div class="mb-3">
				<label for="pwd" class="form-label">Password:</label>
				<input
				  type="password"
				  class="form-control"
				  id="loginPwd"
				  placeholder="Enter password"
				  name="pswd"
				/>
			  </div>
  
			  <button type="submit" class="btn btn-primary" id="loginBtn">
				로그인
			  </button>
			</div>
  
			<!-- Modal footer -->
			<div class="modal-footer">
			  <button
				type="button"
				class="btn btn-danger"
				data-bs-dismiss="modal"
			  >
				Close
			  </button>
			</div>
		  </div>
		</div>
	  </div>


	  <!-- commentModal -->
	  <div class="modal fade" id="commentModal" tabindex="-1" aria-labelledby="commentModalLabel">
		<div class="modal-dialog">
		  <div class="modal-content">
			<div class="modal-header">
			  <h5 class="modal-title" id="commentModalLabel">후기</h5>
			  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
			</div>
			<div class="modal-body">
			  <div class="modal-body" id="commentModalBody">
				<!-- 최근 리뷰가 여기에 표시됩니다. -->
			  </div>
			  <textarea id="commentTextarea" placeholder="후기를 입력하세요..." rows="3" class="form-control"></textarea>
			</div>
			<div class="modal-footer">
			  <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
			  <button type="button" class="btn btn-primary" id="submitComment">후기 남기기</button>
			</div>
		  </div>
		</div>
	  </div>

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
		<script src="./js/index.js"></script>
    <script src="./js/member.js"></script>
  </body>
</html>

 

팀 챌린지 - 쇼핑몰 장바구니 담기 구현해보기

Cart 테이블 생성

use ureca;

drop table if exists cart;

create table cart (
	cartId int primary key NOT NULL auto_increment,
	email varchar(50) NOT NULL,
    prodcode int NOT NULL,
	amount int default 0
);

 

백엔드

1️⃣ 로그인 상태 체크하기 (로그인한 경우에 장바구니 담기 위함)

  • login.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.shop.cafe.dao.LoginDao">
	<!-- ... 생략 -->
	
	<select id="isLogin" resultType="String" parameterType="String">
    	select email from login where token = #{token}
	</select>


</mapper>
  • LoginDao.java
package com.shop.cafe.dao;

import org.apache.ibatis.annotations.Mapper;

import com.shop.cafe.dto.Login;

@Mapper
public interface LoginDao {
	public void insertToken(Login login) throws Exception;

	public void deleteToken(String authorization) throws Exception;
	
	public String isLogin(String token) throws Exception;

}
  • MemberService.java
package com.shop.cafe.service;

import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.shop.cafe.dao.LoginDao;
import com.shop.cafe.dao.MemberDao;
import com.shop.cafe.dto.Login;
import com.shop.cafe.dto.Member;
import com.shop.cafe.util.OpenCrypt;

@Service
public class MemberService {
	@Autowired
	MemberDao memberDao;

	@Autowired
	LoginDao loginDao;

	public String isLogin(String token) throws Exception {
		return loginDao.isLogin(token);
	}

	// ... 생략

}
  • CartController.java
package com.shop.cafe.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import com.shop.cafe.dto.Cart;
import com.shop.cafe.service.CartService;
import com.shop.cafe.service.MemberService;


@RestController
@CrossOrigin("http://127.0.0.1:5500/")
public class CartController {
	
	@Autowired
	MemberService memberService;
	
	@Autowired
	CartService cartService;
	

	@PostMapping("addCart")
	public String addCart(@RequestHeader String authorization, @RequestBody Cart c) {
		System.out.println(authorization);
		try {
			String email = memberService.isLogin(authorization);
			if(email != null) {
				c.setEmail(email); // 추출한 email을 Dto에 설정
				cartService.addCart(c);
				return "장바구니에 추가되었습니다";
			}
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return "장바구니 담기 실패";
		}
		return "장바구니 담기 실패";
	}

}

2️⃣ 장바구니 상태 확인 및 추가(어떤 유저의 특정 상품이 이미 담겨있는 경우에는 수량만 업데이트)

  • cart.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.shop.cafe.dao.CartDao">
	<!-- 장바구니에 해당 상품이 있는지 확인 -->
	<select id="findCartItem" resultType="Cart" parameterType="Cart">
	    select * from cart 
	    where email = #{email} and prodcode = #{prodcode}
	</select>
	
	<!-- 장바구니 수량 업데이트 -->
	<update id="updateCartAmount" parameterType="Cart">
	    update cart 
	    set amount = amount + #{amount}
	    where email = #{email} and prodcode = #{prodcode}
	</update>
	
	<!-- 장바구니에 상품 추가 -->
	<insert id="insertCartItem" parameterType="Cart">
		insert into cart(email, prodcode, amount) values(#{email},#{prodcode},#{amount})
	</insert>


</mapper>
  • Cart.java
package com.shop.cafe.dto;

public class Cart {
	private int cartId, prodcode, amount;
	private String email;

	public Cart(int cartId, int prodcode, int amount, String email) {
		super();
		this.cartId = cartId;
		this.prodcode = prodcode;
		this.amount = amount;
		this.email = email;
	}

	public Cart() {
		super();
		// TODO Auto-generated constructor stub
	}

	public int getCartId() {
		return cartId;
	}

	public void setCartId(int cartId) {
		this.cartId = cartId;
	}

	public int getProdcode() {
		return prodcode;
	}

	public void setProdcode(int prodcode) {
		this.prodcode = prodcode;
	}

	public int getAmount() {
		return amount;
	}

	public void setAmount(int amount) {
		this.amount = amount;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	@Override
	public String toString() {
		return "Cart [cartId=" + cartId + ", prodcode=" + prodcode + ", amount=" + amount + ", email=" + email + "]";
	}

}
  • CartDao.java
package com.shop.cafe.dao;

import org.apache.ibatis.annotations.Mapper;

import com.shop.cafe.dto.Cart;

@Mapper
public interface CartDao {
	public Cart findCartItem(Cart c) throws Exception;
	
	public void updateCartAmount(Cart c) throws Exception;
	
	public void insertCartItem(Cart c) throws Exception;

}
  • CartService.java
package com.shop.cafe.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.shop.cafe.dao.CartDao;
import com.shop.cafe.dto.Cart;

@Service
public class CartService {
	@Autowired
	CartDao cartDao;

	
	public void addCart(Cart c) throws Exception {	
		
		Cart existCart = cartDao.findCartItem(c);
		
		if(existCart != null) {
			cartDao.updateCartAmount(c);
		} else {
			cartDao.insertCartItem(c);
		}
		
	}
}

 

프론트엔드

- 장바구니 담기 버튼에 prodcode를 바탕으로 id를 부여
- 클릭 이벤트를 추가하여 버튼 클릭 시 장바구니 담기 수행
- session storage에 저장되어 있는 토큰을 가져와서 장바구니 담기 post 요청
  • index.js
window.onload = async () => {
  let response = await axios.get("http://localhost:8080/getAllProducts");
  console.log(response);
  let productList = response.data;
  let productListDiv = ``;

  productList.forEach((item) => {
    productListDiv += `<div class="card m-3" style="width: 10rem;">
        <img src="img/${item.pimg}" class="card-img-top" alt="...">
       
        <div class="card-body">
          <b class="card-title">${item.prodname}</b>
          <a href="#" class="review-link" data-product-id="${item.prodcode}" 
          data-bs-toggle="modal" data-bs-target="#commentModal"> 
            <img src="img/review.png" alt="review"> 
          </a>
          <p class="card-text text-danger">${item.price}원</p>   
          <a href="#" class="btn btn-outline-info" id="add-cart-${item.prodcode}">장바구니 담기</a>
        </div>
      </div>`;
  });

  document.getElementById("productListDiv").innerHTML = productListDiv;

  productList.forEach((item) => {
    document
      .getElementById(`add-cart-${item.prodcode}`)
      .addEventListener("click", async () => {
        const Authorization = sessionStorage.getItem("Authorization");
        if (!Authorization) alert("로그인 필요");
        else {
          const data = {
            prodcode: item.prodcode,
            amount: 1,
          };
          const response = await axios.post(
            "http://localhost:8080/addCart",
            data
          );

          alert(response.data);
        }
      });
  });

 // ... 생략
};

 

결과 화면

728x90
반응형

'LG 유플러스 유레카 SW > Spring' 카테고리의 다른 글

[#35] KakaoOAuth 로그인  (0) 2025.03.18
[#33] RESTful 방식 + Swagger 사용  (0) 2025.03.14
[#30] XSS/Tabnabbing 공격  (0) 2025.03.10
[#29] 쇼핑몰 연동 실습 - SQL Injection + Connection Pool  (1) 2025.03.07
[#28] 쇼핑몰 연동 실습 - 다중 서버 세션 문제 해결 + 사용자 인증  (1) 2025.03.06
'LG 유플러스 유레카 SW/Spring' 카테고리의 다른 글
  • [#35] KakaoOAuth 로그인
  • [#33] RESTful 방식 + Swagger 사용
  • [#30] XSS/Tabnabbing 공격
  • [#29] 쇼핑몰 연동 실습 - SQL Injection + Connection Pool
nueos
nueos
  • nueos
    nueos 공부 기록
    nueos
  • 전체
    오늘
    어제
    • 분류 전체보기 (191)
      • 해커톤 (1)
      • 네이버 BoostCamp (6)
      • LG 유플러스 유레카 SW (3)
        • React (21)
        • TypeScript (2)
        • JavaScript (2)
        • HTML+CSS (5)
        • Spring (7)
        • Java (6)
        • SQL (2)
        • Algorithm (8)
        • CX (6)
        • Git (2)
        • 프로젝트 (2)
        • 스터디 (9)
        • 과제 (8)
        • 특강 (1)
      • React (3)
      • Next (0)
      • Javascript (2)
      • HTML (2)
      • CSS (9)
      • Algorithm (6)
      • Database (0)
      • OS (13)
      • C++ (24)
      • Python (1)
      • jQuery (1)
      • Django (1)
      • Git (1)
      • 개발 지식 (3)
      • 정보 보안 (22)
      • 포렌식 (1)
      • 암호 (2)
      • 기타 (4)
      • 패스트캠퍼스 FE 프로젝트십 (5)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    디지털혁신
    heap
    제주해커톤
    exhaustive search
    Stack
    제주지역혁신플랫폼지능형서비스사업단
    디지랩챌린지
    큐
    Queue
    기술로바꾸는세상
    스택
    완전 탐색
    힙
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
nueos
[#31] 토큰 인증 방식
상단으로

티스토리툴바