1. ProductConroller.java
package com.shop.cafe;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
@GetMapping("getAllProducts")
public String getAllProducts() {
return "ok";
}
}
2. Product.java
테이블 스키마를 Product.java 코드의 내용에 담아야 한다 => vo/DTO(데이터를 이동시키는 오브젝트)
package com.shop.cafe.dto;
public class Product {
private int prodcode,price;
private String prodname,pimg;
@Override
public String toString() {
return "product [prodcode=" + prodcode + ", price=" + price + ", prodname=" + prodname + ", pimg=" + pimg + "]";
}
public Product(int prodcode, int price, String prodname, String pimg) {
super();
this.prodcode = prodcode;
this.price = price;
this.prodname = prodname;
this.pimg = pimg;
}
public Product() {
super();
// TODO Auto-generated constructor stub
}
public int getProdcode() {
return prodcode;
}
// set 메서드의 유효성 검사
public void setProdcode(int prodcode) {
this.prodcode = prodcode;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getProdname() {
return prodname;
}
public void setProdname(String prodname) {
this.prodname = prodname;
}
public String getPimg() {
return pimg;
}
public void setPimg(String pimg) {
this.pimg = pimg;
}
}
toString메서드가 있으면 편하다. 왜?
@Override
public String toString() {
return "product [prodcode=" + prodcode + ", price=" + price + ", prodname=" + prodname + ", pimg=" + pimg + "]";
}
표현대로 나오니까
superClass가 있어야하는 이유
public Product() {
super();
// TODO Auto-generated constructor stub
}
default 생성자를 갖고 있기때문에
3. ProductDao.java
DB를 연결해서 query를 날리는 코드이다.
package com.shop.cafe.dao;
import java.sql.*; //JDBC API
import java.util.*;
import com.shop.cafe.dto.Product;
public class ProductDao {
public List<Product> getAllProducts() throws Exception{
// JDBC 6단계
// 1. 드라이버 등록
Class.forName("com.mysql.cj.jdbc.Driver");
// 데이터베이스 연결 정보 설정
String url="jdbc:mysql://localhost:3306/ureca?serverTimezone=UTC";
String user="ureca";
String pw="ureca";
// 실행할 SQL 쿼리
String sql="select * from product";
// try-with-resources를 사용하여 자동으로 리소스 정리
try(
// 2. 데이터베이스 연결
Connection con=DriverManager.getConnection(url,user,pw);
// 3. Statement 생성
PreparedStatement stmt=con.prepareStatement(sql);
// 4. SQL 전송
ResultSet rs=stmt.executeQuery();
) {
// 7. 결과 리스트 생성&결과 받기
List<Product> list=new ArrayList<>();
while(rs.next()) {
// 7-1. 각 행의 데이터를 가져오기
int prodcode=rs.getInt("prodcode");
String prodname=rs.getString("prodname");
String pimg=rs.getString("pimg");
int price=rs.getInt("price");
// Product 객체 생성 후 리스트에 추가
list.add(new Product(prodcode, price, prodname, pimg));
}
// 결과 리스트 반환
return list;
}
}
}
JDBC 코드란?
DB연동 코드
CRUD
3. ProductService.java
ProductDao로부터 똑같은 메서드를 호출한다.
package com.shop.cafe.service;
import java.util.*;
import com.shop.cafe.dao.ProductDao;
import com.shop.cafe.dto.Product;
public class ProductService {
// PD객체 생성
ProductDao productDao;
public List<Product> getAllProducts() throws Exception{
return productDao.getAllProducts();
}
}
public list구문은 하는일 없는거 같은데 public class랑 왜 return으로 반환해 연결하냐?
public list 구문안에 비기능이 많이 들어간다.
비기능이란? 보안, 성능, 트랜잭션 등등
4. Productcontroller.java (수정)
package com.shop.cafe.controller;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Product;
import com.shop.cafe.service.ProductService;
@RestController
public class ProductController {
// DB에서 가져오기 위해
ProductService productService = new ProductService();
@GetMapping("getAllProducts") // url 밑에 gap는 메서드 이름 -> 라우팅한다.
public List<Product> getAllProducts(){
try {
return productService.getAllProducts();
} catch (Exception e) {
e.printStackTrace();
// 디버깅을 위해 null
return null;
}
}
}
자바 데이터를 텍스트로 변환해준다. 자바 객체는 json형태로 표현
웹페이지 -> pc -> ps -> pd-> db => 패턴이라고 한다.
ps넣어야 확장성이 좋아진다.
@RestController란?
응답 tmeplate이 없다.
5. Productcontroller.java (수정)
package com.shop.cafe.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Product;
import com.shop.cafe.service.ProductService;
@RestController
public class ProductController {
// DB에서 가져오기 위해
@Autowired // new라는 코드를 바꿔서 수행해줌 => DI
ProductService productService;
@GetMapping("getAllProducts") // url 밑에 gap는 메서드 이름 -> 라우팅한다.
public List<Product> getAllProducts(){
try {
return productService.getAllProducts();
} catch (Exception e) {
e.printStackTrace();
// 디버깅을 위해 null
return null;
}
}
}
DI란?
쉽게말해서 new한것
=> 의존성을 주입해주겠다.
근데 무조건 다 해주진 않는다. ProductService에서 @Component한 애들만 new해준다.
5-1
package com.shop.cafe.service;
import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.shop.cafe.dao.ProductDao;
import com.shop.cafe.dto.Product;
@Component // component가 있는 녀석들만 new해준다.
public class ProductService { // 비기능이 들어감
// PD객체 생성
@Autowired
ProductDao productDao;
public List<Product> getAllProducts() throws Exception{
return productDao.getAllProducts();
}
}
5-2
package com.shop.cafe.dao;
import java.sql.*;
import java.util.*;
import org.springframework.stereotype.Component;
import com.shop.cafe.dto.Product;
@Component
public class ProductDao {
public List<Product> getAllProducts() throws Exception{
// JDBC 6단계
// 1. 드라이버 등록
Class.forName("com.mysql.cj.jdbc.Driver");
// 데이터베이스 연결 정보 설정
String url="jdbc:mysql://localhost:3306/ureca?serverTimezone=UTC";
String user="ureca";
String pw="ureca";
// 실행할 SQL 쿼리
String sql="select * from product";
// try-with-resources를 사용하여 자동으로 리소스 정리
try(
// 2. 데이터베이스 연결
Connection con=DriverManager.getConnection(url,user,pw);
// 3. Statement 생성
PreparedStatement stmt=con.prepareStatement(sql);
// 4. SQL 전송
ResultSet rs=stmt.executeQuery();
) {
// 7. 결과 리스트 생성&결과 받기
List<Product> list=new ArrayList<>();
while(rs.next()) {
// 7-1. 각 행의 데이터를 가져오기
int prodcode=rs.getInt("prodcode");
String prodname=rs.getString("prodname");
String pimg=rs.getString("pimg");
int price=rs.getInt("price");
// Product 객체 생성 후 리스트에 추가
list.add(new Product(prodcode, price, prodname, pimg));
}
// 결과 리스트 반환
return list;
}
}
}
5-3
package com.shop.cafe.service;
import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.shop.cafe.dao.ProductDao;
import com.shop.cafe.dto.Product;
@Service // component라고 정해도되나 개발자 편의성을 위해 Service라고 변경
public class ProductService { // 비기능이 들어감
// PD객체 생성
@Autowired
ProductDao productDao;
public List<Product> getAllProducts() throws Exception{
return productDao.getAllProducts();
}
}
window.onload = async () => {
// fetch를 하러가는 애를 XMLHttpReguest(XHR)이라고 부른다. forEach로 바로 내려간다. 거기서 기다릴게요가 await이다. await는 async 블록 내에서만 사용 가능하다.
let productList = await fetch('http://localhost:8080/getAllProducts', {
method: 'GET',
});
// productList가 위에서는 text형태이고 밑에서는 배열 형태이다. 배열로 바꾸는 작업 필요
productList = await productList.json();
let productListDiv = ``;
productList.forEach((item) => {
productListDiv += `<div class="card mt-2" style="width: 10rem;">
<img src="img/${item.pimg}" class="card-img-top" alt="${item.prodname}" height="150">
<div class="card-body">
<b class="card-title">${item.prodname}</b><br>
<span class="card-text text-danger">${item.price}원</span>
<a href="#" class="btn btn-outline-info">담기</a>
</div>
</div>`;
});
document.getElementById('productListDiv').innerHTML = productListDiv;
};
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
document.getElementById(
'loginSpan'
).innerHTML = `<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>`;
document.getElementById('signupLi').remove();
}
});
prodlist는 응답객체이다. 응답 객체안에있는 응답 받은 애들만 json에서 자바스크립트로 바꿔줘 하면 productlist에서 배열(forEach)로 바꿔준다.
중요 정보 처리하기
해커는 파일이름이 중요해보이는걸 공격한다.
DB_DRIVER=com.mysql.cj.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/ureca?serverTimezone=UTC
DB_USER=ureca
DB_PW=ureca
DB_DRIVER=com.mysql.cj.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/ureca?serverTimezone=UTC
DB_USER=ureca
DB_PW=ureca
package com.shop.cafe;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
@SpringBootApplication
@PropertySource("classpath:config/secu.properties")
public class CafeApplication {
public static void main(String[] args) {
SpringApplication.run(CafeApplication.class, args);
}
}
classpath를 통해 config에서 중요 정보 처리한 것을 연결시킨다.
보안에서 정석은 없다!
자동 와이어링
package com.shop.cafe.dao;
import java.sql.*;
import java.util.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import com.shop.cafe.dto.Product;
@Repository // 객체 생성을 프레임워크가 해주는 것
public class ProductDao {
// new로 수동 객체 생성을 하면 @Value에서 인식을 못한다.
@Value("${spring.datasource.driver-class-name}")
private String DB_DRIVER;
@Value("${spring.datasource.url}")
private String DB_URL;
@Value("${spring.datasource.username}")
private String DB_USER;
@Value("${spring.datasource.password}")
private String DB_PW;
public List<Product> getAllProducts() throws Exception{
// 실행할 SQL 쿼리
String sql="select * from product";
// JDBC 6단계
// 1. 드라이버 등록
Class.forName(DB_DRIVER); //
// try-with-resources를 사용하여 자동으로 리소스 정리
try(
// 2. 데이터베이스 연결
Connection con = DriverManager.getConnection(DB_URL, DB_USER, DB_PW);
// 3. Statement 생성
PreparedStatement stmt = con.prepareStatement(sql);
// 4. SQL 전송 및 실행
ResultSet rs = stmt.executeQuery();
) {
// 5. 결과 리스트 생성 & 결과 받기
List<Product> list = new ArrayList<>(); //
while(rs.next()) {
// 5-1. 각 행의 데이터를 가져오기
int prodcode = rs.getInt("prodcode");
String prodname = rs.getString("prodname");
String pimg = rs.getString("pimg");
int price = rs.getInt("price");
// 6. Product 객체 생성 후 리스트에 추가
list.add(new Product(prodcode, price, prodname, pimg));
}
// 7. 결과 리스트 반환
return list;
}
}
}
git과 현재 내 스프링 프로젝트랑 연결한다.
.gitignore 파일이 생기는데
**/config/*.properties
위에와 같이 작성해준뒤 저장해야 중요 정보들은 git에 안들어간다.
회원가입
MemberController.java
package com.shop.cafe.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MemberController {
@GetMapping("insertMember") // url 매핑
public String insertMember() {
return "ok";
}
}
package com.shop.cafe.dto;
import java.util.Date;
public class Member {
private String email, pwd, nickname;
private Date registDate;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getRegistDate() {
return registDate;
}
public void setRegistDate(Date registDate) {
this.registDate = registDate;
}
public Member() {
super();
// TODO Auto-generated constructor stub
}
public Member(String email, String pwd, String nickname, Date registDate) {
super();
this.email = email;
this.pwd = pwd;
this.nickname = nickname;
this.registDate = registDate;
}
@Override
public String toString() {
return "Member [email=" + email + ", pwd=" + pwd + ", nickname=" + nickname + ", registDate=" + registDate
+ "]";
}
}
dao가 insert문을 수행하려고 한다. 그러면 데이터가 이썽야하는데 그것을 서비스에서 시작한다.
package com.shop.cafe.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.Statement;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import com.shop.cafe.dto.Member;
@Repository
public class MemberDao {
@Value("${spring.datasource.driver-class-name}")
private String DB_DRIVER;
@Value("${spring.datasource.url}")
private String DB_URL;
@Value("${spring.datasource.username}")
private String DB_USER;
@Value("${spring.datasource.password}")
private String DB_PW;
public void insertMember(Member m) throws Exception {
Class.forName(DB_DRIVER);
String sql="insert into member(nickname, pwd, email) values(?,?,?)";
try(
Connection con=DriverManager.getConnection(DB_URL,DB_USER,DB_PW);
PreparedStatement stmt=con.prepareStatement(sql);
){
stmt.setString(1, m.getNickname());
stmt.setString(2, m.getPwd());
stmt.setString(3, m.getEmail());
int i=stmt.executeUpdate();
System.out.println(i+"행이 insert되었습니다");
}
}
}
package com.shop.cafe.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.shop.cafe.dao.MemberDao;
import com.shop.cafe.dto.Member;
@Service
public class MemberService {
@Autowired
MemberDao memberDao;
public void insertMember(Member m) throws Exception {
memberDao.insertMember(m);
}
}
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Member;
import com.shop.cafe.service.MemberService;
@RestController
@CrossOrigin("http://172.30.1.46:5500/")
public class MemberController {
@Autowired
MemberService memberService;
@PostMapping("insertMember") // url 매핑 getMapping은 보안에 나쁨으로 PostMapping으로 처리한다.
// 브라우저로 부터 받아오는 정보를 표시할 수 있다.
public Map<String, String> insertMember(@RequestBody Member m) {
Map<String, String> responseData = new HashMap();
//에러가 나면 catch블록
try {
memberService.insertMember(m);
responseData.put("msg", "ok");
} catch (Exception e) {
e.printStackTrace();
responseData.put("msg", e.getMessage());
}
return responseData;
}
}
getMapping은 보안에 왜 나쁠까?
url에 데이터가 노출됨으로
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
document.getElementById(
'loginSpan'
).innerHTML = `<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>`;
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
로그인
MemberController.java
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Member;
import com.shop.cafe.service.MemberService;
@RestController
@CrossOrigin("http://172.30.1.46:5500/")
public class MemberController {
// 회원가입
@Autowired
MemberService memberService;
@PostMapping("insertMember") // url 매핑 getMapping은 보안에 나쁨으로 PostMapping으로 처리한다.
// 브라우저로 부터 받아오는 정보를 표시할 수 있다.
public Map<String, String> insertMember(@RequestBody Member m) {
Map<String, String> responseData = new HashMap();
//에러가 나면 catch블록
try {
memberService.insertMember(m);
responseData.put("msg", "ok");
} catch (Exception e) {
e.printStackTrace();
responseData.put("msg", e.getMessage());
}
return responseData;
}
// 로그인
@GetMapping("login")
public String login() {
return "ok";
}
}
사용자의 아이디랑 비번은 GM이 아닌 PM으로 받겠다. 그 이유는? URL 노출 막기위해
Member.java의 코드를 보면 private으로 되어있기에 set메서드를 통해 들어간다.
memberService.java
package com.shop.cafe.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.shop.cafe.dao.MemberDao;
import com.shop.cafe.dto.Member;
@Service
public class MemberService {
@Autowired
MemberDao memberDao;
public void insertMember(Member m) throws Exception {
memberDao.insertMember(m);
}
// md한테 로그인한다고 알려줌
public Member login(Member m) throws Exception {
// 이것도 md 메서드 호출됨
return memberDao.login(m);
}
}
memberController.java
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Member;
import com.shop.cafe.service.MemberService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@RestController
@CrossOrigin("http://172.30.1.46:5500/")
public class MemberController {
// 회원가입
@Autowired
MemberService memberService;
@PostMapping("insertMember") // url 매핑 getMapping은 보안에 나쁨으로 PostMapping으로 처리한다.
// 브라우저로 부터 받아오는 정보를 표시할 수 있다.
public Map<String, String> insertMember(@RequestBody Member m) {
Map<String, String> responseData = new HashMap();
//에러가 나면 catch블록
try {
memberService.insertMember(m);
responseData.put("msg", "ok");
} catch (Exception e) {
e.printStackTrace();
responseData.put("msg", e.getMessage());
}
return responseData;
}
// 로그인
@PostMapping("login")
// pm으로 받으면 body 안에 들아간다.
public Map<String, String> login(@RequestBody Member m, HttpServletRequest request) {
Map<String, String> responseData = new HashMap();
// 멤버 서비스에 넘겨서 로그인 해달라고 요청
// MS에서 로그인 메서드 만들어야함 -> 만듬
try {
m= memberService.login(m);
// 어차피 같은 객체이므로 m이라고 함
if(m!=null) { // login ok
// 해당하는 session에 k,v넣어준다.
HttpSession session=request.getSession();
System.out.println(session.getId());
session.setAttribute("member", m);
responseData.put("msg","ok");
} else {
//login fail
responseData.put("msg", "다시 로그인 해주세요");
}
} catch (Exception e) { // login error
// TODO Auto-generated catch block
e.printStackTrace();
responseData.put("msg", "다시 로그인 해주세요");
}
return responseData;
}
}
메모리에 로그인을 기록하는 공간 HttpSection
사물함과 같은 역할을 한다.
memberDao.java
package com.shop.cafe.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import com.shop.cafe.dto.Member;
@Repository // = @Component
public class MemberDao {
@Value("${spring.datasource.driver-class-name}")
private String DB_DRIVER;
@Value("${spring.datasource.url}")
private String DB_URL;
@Value("${spring.datasource.username}")
private String DB_USER;
@Value("${spring.datasource.password}")
private String DB_PW;
// 회원가입 기능
public void insertMember(Member m) throws Exception {
Class.forName(DB_DRIVER);
// SQL Injection 방지를 위해 '?' 사용
String sql="insert into member(nickname, pwd, email) values(?,?,?)";
try(
Connection con=DriverManager.getConnection(DB_URL,DB_USER,DB_PW);
PreparedStatement stmt=con.prepareStatement(sql);
){
stmt.setString(1, m.getNickname());
stmt.setString(2, m.getPwd());
stmt.setString(3, m.getEmail());
int i=stmt.executeUpdate();
System.out.println(i+"행이 insert되었습니다");
}
}
// 로그인 기능
public Member login(Member m) throws Exception {
Class.forName(DB_DRIVER);
String sql="select * from member where email='"+m.getEmail()+"' and pwd='"+m.getPwd()+"' ";
try(
Connection con=DriverManager.getConnection(DB_URL,DB_USER,DB_PW);
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery(sql); // SQL 실행
){
if(rs.next()) {//login ok
String nickname=rs.getString("nickname");
m.setNickname(nickname);
return m;
}else {
return null;
}
}
}
}
F/W는 default생성자를 통해 new를 한다. md의 경우 @Repository가 있다. 이건 new를 대신한다.
그런데 왜? md에서는 component를 하지않고 java bean형태로 만들었을까?
자바빈은 vo나 dto는 객체화를 통해 작업한다? 즉, 이름 규칙
이름 규칙? 변수 + setter+getter
모든 클래스를 외부f.w에서 제어x
특별한 클래스만 제어하려고 한다. (특별한 타입을 지정하겠다 => 인터페이스)
그래서 인터페이스를 한다.
로그인을 하면 sessionID가 springbood console창에 생긴다.
sessionId에서 8080을 받았으니까 application영역의 이름에 떠야한다.
그런데 웹은 8080에서 받은게 아니라 5500에서 받았기에 안뜨는 것이다.
이처럼 프론트엔드와 백엔드가 분리되어있을때 안뜨는 문제점이 생긴다.
웹 브라우저 정보 저장
1. 쿠키 (수명 짧)
2. sessionStorage (수명 짧)
3. localstorage (수명 김)
=> 3개다 보안 bad
다중 서버에서 세션 문제 해결
<!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>
window.onload = async () => {
axios.defaults.withCredentials = true;
// fetch를 하러가는 애를 XMLHttpReguest(XHR)이라고 부른다. forEach로 바로 내려간다. 거기서 기다릴게요가 await이다. await는 async 블록 내에서만 사용 가능하다.
let productList = await fetch('http://localhost:8080/getAllProducts', {
method: 'GET',
});
console.log(productList);
// productList가 위에서는 text형태이고 밑에서는 배열 형태이다. 배열로 바꾸는 작업 필요
productList = await productList.json();
let productListDiv = ``;
productList.forEach((item) => {
productListDiv += `<div class="card mt-2" style="width: 10rem;">
<img src="img/${item.pimg}" class="card-img-top" alt="${item.prodname}" height="150">
<div class="card-body">
<b class="card-title">${item.prodname}</b><br>
<span class="card-text text-danger">${item.price}원</span>
<a href="#" class="btn btn-outline-info">담기</a>
</div>
</div>`;
});
document.getElementById('productListDiv').innerHTML = productListDiv;
};
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
/**
// document.getElementById(
// 'loginSpan'
// ).innerHTML = `<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>`;
*/
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
// document.getElementById("loginSpan").innerHTML = `<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>`;
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('loginBtn').addEventListener('click', async () => {
const email = document.getElementById('loginEmail').value;
const pwd = document.getElementById('loginPwd').value;
const data = { email, pwd };
// await fectch -> await axios.post
let response = await axios.post('http://localhost:8080/login', data);
//response = await response.json();
console.log(response);
alert(response.data.msg);
});
MyConfig
package com.shop.cafe;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
// WMC -> MyConfigm, @Configuration -> MyConfig
@Configuration
public class MyConfig implements WebMvcConfigurer{
@Override
public void addCorsMappings(CorsRegistry registry) {
// 모든 url
registry.addMapping("/**")
// 요청 방식(get, post etc)
.allowedOrigins("http://172.30.1.46:5500/ ")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true); //쿠키, 세션 정보도 허용
}
}
두개의 숫자가 일치하다.
도메인과 쿠키가 같이 들어온다.
쿠키 안에는 jessetionID포함 => sessionID로 계속 메모리에 접근
로그인 화면에 나타나게 하기 그리고 로그인 창 사라지게하기
window.onload = async () => {
axios.defaults.withCredentials = true;
// fetch를 하러가는 애를 XMLHttpReguest(XHR)이라고 부른다. forEach로 바로 내려간다. 거기서 기다릴게요가 await이다. await는 async 블록 내에서만 사용 가능하다.
let productList = await fetch('http://localhost:8080/getAllProducts', {
method: 'GET',
});
console.log(productList);
// productList가 위에서는 text형태이고 밑에서는 배열 형태이다. 배열로 바꾸는 작업 필요
productList = await productList.json();
let productListDiv = ``;
productList.forEach((item) => {
productListDiv += `<div class="card mt-2" style="width: 10rem;">
<img src="img/${item.pimg}" class="card-img-top" alt="${item.prodname}" height="150">
<div class="card-body">
<b class="card-title">${item.prodname}</b><br>
<span class="card-text text-danger">${item.price}원</span>
<a href="#" class="btn btn-outline-info">담기</a>
</div>
</div>`;
});
document.getElementById('productListDiv').innerHTML = productListDiv;
};
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
/**
// document.getElementById(
// 'loginSpan'
// ).innerHTML = `<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>`;
*/
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
// document.getElementById("loginSpan").innerHTML = `<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>`;
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('loginBtn').addEventListener('click', async () => {
const email = document.getElementById('loginEmail').value;
const pwd = document.getElementById('loginPwd').value;
const data = { email, pwd };
// await fectch -> await axios.post
let response = await axios.post('http://localhost:8080/login', data);
//response = await response.json();
console.log(response);
alert(response.data.msg);
if ((response.data.msg = 'ok')) {
// 로그인창 사라지게
const modal = bootstrap.Modal.getInstance(
document.getElementById('loginModal')
);
modal.hide();
// 화면에 이메일 표시
document.getElementById('loginSpan').innerHTML = email;
}
});
쿠키랑 sessionStorage의 차이점
sessionstroage은 창이 화면에 그대로 남아있을때만 유지되고
쿠키는 새 요청할때마다 갱신된다.
브라우저를 새로고침하면 화면의 login이 풀려보이는 문제 sessionstorage로 해결
window.onload = async () => {
// 세션 저장소 유지되는 코드
const email = sessionStorage.getItem('email');
if (email) {
window.onload = async () => {
axios.defaults.withCredentials = true;
// fetch를 하러가는 애를 XMLHttpReguest(XHR)이라고 부른다. forEach로 바로 내려간다. 거기서 기다릴게요가 await이다. await는 async 블록 내에서만 사용 가능하다.
let productList = await fetch('http://localhost:8080/getAllProducts', {
method: 'GET',
});
console.log(productList);
// productList가 위에서는 text형태이고 밑에서는 배열 형태이다. 배열로 바꾸는 작업 필요
productList = await productList.json();
let productListDiv = ``;
productList.forEach((item) => {
productListDiv += `<div class="card mt-2" style="width: 10rem;">
<img src="img/${item.pimg}" class="card-img-top" alt="${item.prodname}" height="150">
<div class="card-body">
<b class="card-title">${item.prodname}</b><br>
<span class="card-text text-danger">${item.price}원</span>
<a href="#" class="btn btn-outline-info">장바구니 담기</a>
</div>
</div>`;
});
document.getElementById('productListDiv').innerHTML = productListDiv;
};
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
/**
// document.getElementById(
// 'loginSpan'
// ).innerHTML = `<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>`;
*/
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
// document.getElementById("loginSpan").innerHTML = `<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>`;
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('loginBtn').addEventListener('click', async () => {
const email = document.getElementById('loginEmail').value;
const pwd = document.getElementById('loginPwd').value;
const data = { email, pwd };
// await fectch -> await axios.post
let response = await axios.post('http://localhost:8080/login', data);
//response = await response.json();
console.log(response);
alert(response.data.msg);
if ((response.data.msg = 'ok')) {
// 로그인창 사라지게
const modal = bootstrap.Modal.getInstance(
document.getElementById('loginModal')
);
modal.hide();
// 화면에 이메일 표시
document.getElementById('loginSpan').innerHTML = email;
}
});
window.onload = async () => {
axios.defaults.withCredentials = true;
// fetch를 하러가는 애를 XMLHttpReguest(XHR)이라고 부른다. forEach로 바로 내려간다. 거기서 기다릴게요가 await이다. await는 async 블록 내에서만 사용 가능하다.
let productList = await fetch('http://localhost:8080/getAllProducts', {
method: 'GET',
});
console.log(productList);
// productList가 위에서는 text형태이고 밑에서는 배열 형태이다. 배열로 바꾸는 작업 필요
productList = await productList.json();
let productListDiv = ``;
productList.forEach((item) => {
productListDiv += `<div class="card mt-2" style="width: 10rem;">
<img src="img/${item.pimg}" class="card-img-top" alt="${item.prodname}" height="150">
<div class="card-body">
<b class="card-title">${item.prodname}</b><br>
<span class="card-text text-danger">${item.price}원</span>
<a href="#" class="btn btn-outline-info">담기</a>
</div>
</div>`;
});
document.getElementById('productListDiv').innerHTML = productListDiv;
};
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
/**
// document.getElementById(
// 'loginSpan'
// ).innerHTML = `<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>`;
*/
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
// document.getElementById("loginSpan").innerHTML = `<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>`;
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('loginBtn').addEventListener('click', async () => {
const email = document.getElementById('loginEmail').value;
const pwd = document.getElementById('loginPwd').value;
const data = { email, pwd };
// await fectch -> await axios.post
let response = await axios.post('http://localhost:8080/login', data);
//response = await response.json();
console.log(response);
alert(response.data.msg);
if ((response.data.msg = 'ok')) {
// 로그인창 사라지게
const modal = bootstrap.Modal.getInstance(
document.getElementById('loginModal')
);
modal.hide();
// 화면에 이메일 표시
document.getElementById('loginSpan').innerHTML = email;
}
});
document.getElementById('loginSpan').innerHTML = email;
}
axios.defaults.withCredentials = true;
// fetch를 하러가는 애를 XMLHttpReguest(XHR)이라고 부른다. forEach로 바로 내려간다. 거기서 기다릴게요가 await이다. await는 async 블록 내에서만 사용 가능하다.
let productList = await fetch('http://localhost:8080/getAllProducts', {
method: 'GET',
});
console.log(productList);
// productList가 위에서는 text형태이고 밑에서는 배열 형태이다. 배열로 바꾸는 작업 필요
productList = await productList.json();
let productListDiv = ``;
productList.forEach((item) => {
productListDiv += `<div class="card mt-2" style="width: 10rem;">
<img src="img/${item.pimg}" class="card-img-top" alt="${item.prodname}" height="150">
<div class="card-body">
<b class="card-title">${item.prodname}</b><br>
<span class="card-text text-danger">${item.price}원</span>
<a href="#" class="btn btn-outline-info">담기</a>
</div>
</div>`;
});
document.getElementById('productListDiv').innerHTML = productListDiv;
};
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
/**
// document.getElementById(
// 'loginSpan'
// ).innerHTML = `<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>`;
*/
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('signupBtn').addEventListener('click', async () => {
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data),
});
response = await response.json();
console.log(response);
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
//hero icons
// document.getElementById("loginSpan").innerHTML = `<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>`;
document.getElementById('signupLi').remove();
} else {
alert(response.msg);
}
});
document.getElementById('loginBtn').addEventListener('click', async () => {
const email = document.getElementById('loginEmail').value;
const pwd = document.getElementById('loginPwd').value;
const data = { email, pwd };
// await fectch -> await axios.post
let response = await axios.post('http://localhost:8080/login', data);
//response = await response.json();
console.log(response);
alert(response.data.msg);
if ((response.data.msg = 'ok')) {
// 로그인창 사라지게
const modal = bootstrap.Modal.getInstance(
document.getElementById('loginModal')
);
modal.hide();
// 화면에 이메일 표시
document.getElementById('loginSpan').innerHTML = email;
}
});
window.onload = async () => {
// // 세션 저장소 유지되는 코드, 세션에서 이메일 가져와서 로그인 상태 확인하는 코드
const email = sessionStorage.getItem('email');
// 로그인한 유저면 이메일 + 로그아웃 버튼 화면에 표시
if (email) {
document.getElementById('loginSpan').innerHTML =
email + ` <button id="logout">logout</button>`;
}
// axios에서 withCredentials 설정 (쿠키 포함해서 요청 보낼 수 있게)
axios.defaults.withCredentials = true;
console.log(axios); // axios가 잘 적용됐는지 콘솔에서 확인
// fetch를 하러가는 애를 XMLHttpReguest(XHR)이라고 부른다. forEach로 바로 내려간다. 거기서 기다릴게요가 await이다. await는 async 블록 내에서만 사용 가능하다.
// 서버에서 상품 리스트 가져오기 (GET 요청)
let productList = await fetch('http://localhost:8080/getAllProducts', {
method: 'GET',
});
console.log(productList); // 응답 객체 확인
// productList가 위에서는 text형태이고 밑에서는 배열 형태이다. 배열로 바꾸는 작업 필요
// 응답 데이터를 JSON으로 변환
productList = await productList.json();
console.log(productList); // 변환된 데이터 확인
// 상품 목록 HTML로 만들어서 화면에 추가
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>
<p class="card-text text-danger">${item.price}원</p>
<a href="#" class="btn btn-outline-info" id="addCart">장바구니 담기</a>
</div>
</div>`;
});
// 만든 HTML을 실제 웹 페이지에 삽입
document.getElementById('productListDiv').innerHTML = productListDiv;
};
// 회원가입 버튼 클릭 이벤트
document.getElementById('signupBtn').addEventListener('click', async () => {
// 입력값 가져오기
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
// 서버에 회원가입 요청 보내기
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
// 응답 데이터를 JSON으로 변환
response = await response.json();
console.log(response); // 응답 확인
// 회원가입 성공하면 모달 닫고, 버튼 제거
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
document.getElementById('signupLi').remove();
} else {
alert(response.msg); // 회원가입 실패 메시지
}
});
// 로그인 버튼 클릭 이벤트
document.getElementById('loginBtn').addEventListener('click', async () => {
// 입력값 가져오기
const email = document.getElementById('loginEmail').value;
const pwd = document.getElementById('loginPwd').value;
const data = { email, pwd };
// 서버에 로그인 요청 보내기
let response = await axios.post('http://localhost:8080/login', data);
console.log(response); // 응답 확인
alert(response.data.msg); // 로그인 결과 메시지
// 로그인 성공하면 이메일 표시 + 로그아웃 버튼 추가
if (response.data.msg === 'ok') {
// 로그인창 사라지게
const modal = bootstrap.Modal.getInstance(
document.getElementById('loginModal')
);
modal.hide();
// 화면에 이메일 표시
document.getElementById('loginSpan').innerHTML =
email + ` <button id="logout">logout</button>`;
sessionStorage.setItem('email', email); // 세션에 로그인 정보 저장
}
});
// 상품 목록에서 "장바구니 담기" 버튼 클릭 이벤트
document.getElementById('productListDiv').addEventListener('click', (event) => {
// 클릭한 요소의 id가 "addCart"이면 실행
if (event.target.id == 'addCart') {
axios.post('http://localhost:8080/addCart', {}); // 장바구니에 추가 요청 보내기
}
});
// 로그아웃 버튼 클릭 이벤트
document.getElementById('loginSpan').addEventListener('click', (event) => {
// 로그아웃 버튼을 클릭하면 실행
if (event.target.id == 'logout') {
sessionStorage.removeItem('email'); // 세션에서 이메일 삭제
window.location.reload(); // 새로고침해서 로그아웃 상태 반영
}
});
위의 문제를 해결하기 위해!
CartController.java
생성하기
package com.shop.cafe.controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin("http://172.30.1.46:5500/")
public class CartController {
@GetMapping("addCart")
public String addCart() {
return "ok";
}
}
// 로그아웃
<!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>
</body>
</html>
window.onload = async () => {
// // 세션 저장소 유지되는 코드, 세션에서 이메일 가져와서 로그인 상태 확인하는 코드
const email = sessionStorage.getItem('email');
// 로그인한 유저면 이메일 + 로그아웃 버튼 화면에 표시
if (email) {
document.getElementById('loginSpan').innerHTML =
email + ` <button id="logout">logout</button>`;
}
// axios에서 withCredentials 설정 (쿠키 포함해서 요청 보낼 수 있게)
axios.defaults.withCredentials = true;
console.log(axios); // axios가 잘 적용됐는지 콘솔에서 확인
// fetch를 하러가는 애를 XMLHttpReguest(XHR)이라고 부른다. forEach로 바로 내려간다. 거기서 기다릴게요가 await이다. await는 async 블록 내에서만 사용 가능하다.
// 서버에서 상품 리스트 가져오기 (GET 요청)
let productList = await fetch('http://localhost:8080/getAllProducts', {
method: 'GET',
});
console.log(productList); // 응답 객체 확인
// productList가 위에서는 text형태이고 밑에서는 배열 형태이다. 배열로 바꾸는 작업 필요
// 응답 데이터를 JSON으로 변환
productList = await productList.json();
console.log(productList); // 변환된 데이터 확인
// 상품 목록 HTML로 만들어서 화면에 추가
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>
<p class="card-text text-danger">${item.price}원</p>
<a href="#" class="btn btn-outline-info" id="addCart">장바구니 담기</a>
</div>
</div>`;
});
// 만든 HTML을 실제 웹 페이지에 삽입
document.getElementById('productListDiv').innerHTML = productListDiv;
};
// 회원가입 버튼 클릭 이벤트
document.getElementById('signupBtn').addEventListener('click', async () => {
// 입력값 가져오기
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
// 서버에 회원가입 요청 보내기
let response = await fetch('http://localhost:8080/insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
// 응답 데이터를 JSON으로 변환
response = await response.json();
console.log(response); // 응답 확인
// 회원가입 성공하면 모달 닫고, 버튼 제거
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
document.getElementById('signupLi').remove();
} else {
alert(response.msg); // 회원가입 실패 메시지
}
});
// 로그인 버튼 클릭 이벤트
document.getElementById('loginBtn').addEventListener('click', async () => {
// 입력값 가져오기
const email = document.getElementById('loginEmail').value;
const pwd = document.getElementById('loginPwd').value;
const data = { email, pwd };
// 서버에 로그인 요청 보내기
let response = await axios.post('http://localhost:8080/login', data);
console.log(response); // 응답 확인
alert(response.data.msg); // 로그인 결과 메시지
// 로그인 성공하면 이메일 표시 + 로그아웃 버튼 추가
if (response.data.msg === 'ok') {
// 로그인창 사라지게
const modal = bootstrap.Modal.getInstance(
document.getElementById('loginModal')
);
modal.hide();
// 화면에 이메일 표시
document.getElementById('loginSpan').innerHTML =
email + ` <button id="logout">logout</button>`;
sessionStorage.setItem('email', email); // 세션에 로그인 정보 저장
}
});
// 상품 목록에서 "장바구니 담기" 버튼 클릭 이벤트
document.getElementById('productListDiv').addEventListener('click', (event) => {
// 클릭한 요소의 id가 "addCart"이면 실행
if (event.target.id == 'addCart') {
axios.post('http://localhost:8080/addCart', {}); // 장바구니에 추가 요청 보내기
}
});
// 로그아웃 버튼 클릭 이벤트
document.getElementById('loginSpan').addEventListener('click', (event) => {
// 로그아웃 버튼을 클릭하면 실행
if (event.target.id == 'logout') {
sessionStorage.removeItem('email'); // 세션에서 이메일 삭제
window.location.reload(); // 새로고침해서 로그아웃 상태 반영
}
});
// 장바구니 담기
cart.java
package com.shop.cafe.dto;
public class Cart {
private String email;
private int prodcode, quantity;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getProdcode() {
return prodcode;
}
public void setProdcode(int prodcode) {
this.prodcode = prodcode;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public Cart(String email, int prodcode, int quantity) {
super();
this.email = email;
this.prodcode = prodcode;
this.quantity = quantity;
}
public Cart() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Cart [email=" + email + ", prodcode=" + prodcode + ", quantity=" + quantity + "]";
}
}
cartController.java
package com.shop.cafe.controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@RestController
@CrossOrigin("http://172.30.1.46:5500/")
public class CartController {
@PostMapping("addCart")
public String addCart(HttpServletRequest request ) {
HttpSession session = request.getSession(false);
System.out.println("addCart() 호출됨: "+session);
return "ok";
}
}
cartDao
package com.shop.cafe.dao;
import org.apache.ibatis.annotations.Mapper;
import com.shop.cafe.dto.Cart;
@Mapper
public interface CartDao {
public void addToCart(Cart cart) throws Exception;
}
장바구니 담기 클릭시 null
5500에서 :8080으로부터 받은 JSESSIONID를 저장했어도 다음 요청 시 쿠키를 가지고 가지 못하는 문제 해결
1 : 구조 변경 방법
resources 파일 -> static 폴더 생성 후 html, css, js 파일 넣어준다.
그 뒤 ip 주소에서 5050을 8080으로 수정해주면 된다.
MyConfig.java
package com.shop.cafe;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
// WMC -> MyConfigm, @Configuration -> MyConfig
@Configuration
public class MyConfig implements WebMvcConfigurer{
@Override
public void addCorsMappings(CorsRegistry registry) {
// 모든 url
registry.addMapping("/**")
// 요청 방식(get, post etc)
.allowedOrigins("http://172.30.1.46:8080/ ")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true); //쿠키, 세션 정보도 허용
}
}
CartController.java
package com.shop.cafe.controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@RestController
@CrossOrigin("http://172.30.1.46:8080/")
public class CartController {
@PostMapping("addCart")
public String addCart(HttpServletRequest request ) {
HttpSession session = request.getSession(false);
System.out.println("addCart() 호출됨: "+session);
return "ok";
}
}
MemberController.java
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Member;
import com.shop.cafe.service.MemberService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@RestController
@CrossOrigin("http://172.30.1.46:8080/")
public class MemberController {
// 회원가입
@Autowired
MemberService memberService;
@PostMapping("insertMember") // url 매핑 getMapping은 보안에 나쁨으로 PostMapping으로 처리한다.
// 브라우저로 부터 받아오는 정보를 표시할 수 있다.
public Map<String, String> insertMember(@RequestBody Member m) {
Map<String, String> responseData = new HashMap();
//에러가 나면 catch블록
try {
memberService.insertMember(m);
responseData.put("msg", "ok");
} catch (Exception e) {
e.printStackTrace();
responseData.put("msg", e.getMessage());
}
return responseData;
}
// 로그인
@PostMapping("login")
// pm으로 받으면 body 안에 들아간다.
public Map<String, String> login(@RequestBody Member m, HttpServletRequest request) {
System.out.println(m);
Map<String, String> responseData = new HashMap();
// 멤버 서비스에 넘겨서 로그인 해달라고 요청
// MS에서 로그인 메서드 만들어야함 -> 만듬
try {
m= memberService.login(m);
// 어차피 같은 객체이므로 m이라고 함
if(m!=null) { // login ok
// 해당하는 session에 k,v넣어준다.
HttpSession session=request.getSession();
System.out.println(session.getId());
session.setAttribute("member", m);
responseData.put("msg","ok");
} else {
//login fail
responseData.put("msg", "다시 로그인 해주세요");
}
} catch (Exception e) { // login error
// TODO Auto-generated catch block
e.printStackTrace();
responseData.put("msg", "다시 로그인 해주세요");
}
return responseData;
}
}
package com.shop.cafe.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Product;
import com.shop.cafe.service.ProductService;
@RestController
@CrossOrigin("http://172.30.1.46:8080/")
public class ProductController {
// DB에서 가져오기 위해
@Autowired // new라는 코드를 바꿔서 수행해줌 => DI
ProductService productService;
@GetMapping("getAllProducts") // url 밑에 gap는 메서드 이름 -> 라우팅한다.
public List<Product> getAllProducts(){
try {
return productService.getAllProducts();
} catch (Exception e) {
e.printStackTrace();
// 디버깅을 위해 null
return null;
}
}
}
로그인 성공
장바구니 담기 성공!
세션 저장소에 로그인 정보 있다.
로그 아웃을 하면 세션 정보가 사라진다.
하지만 addCart를 누르면 쿠키가 포함되지 않은 문제점을 발견하게 된다.
이를 해결하기 몇몇개를 주석 처리한다.
package com.shop.cafe;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
// WMC -> MyConfigm, @Configuration -> MyConfig
// @Configuration
public class MyConfig implements WebMvcConfigurer{
// @Override
// public void addCorsMappings(CorsRegistry registry) {
// // 모든 url
// registry.addMapping("/**")
// // 요청 방식(get, post etc)
// .allowedOrigins("http://172.30.1.46:8080/ ")
// .allowedMethods("*")
// .allowedHeaders("*")
// .allowCredentials(true); //쿠키, 세션 정보도 허용
// }
}
여기서는 configuration, MyConfig 메서드 안의 내용을 주석 처리하고
CartController, MemberContoller, ProductController에서 각각의 @CrossOrigin를 주석처리한다
package com.shop.cafe.controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@RestController
// @CrossOrigin("http://172.30.1.46:8080/")
public class CartController {
@PostMapping("addCart")
public String addCart(HttpServletRequest request ) {
HttpSession session = request.getSession(false);
System.out.println("addCart() 호출됨: "+session);
return "ok";
}
}
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Member;
import com.shop.cafe.service.MemberService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@RestController
// @CrossOrigin("http://172.30.1.46:8080/")
public class MemberController {
// 회원가입
@Autowired
MemberService memberService;
@PostMapping("insertMember") // url 매핑 getMapping은 보안에 나쁨으로 PostMapping으로 처리한다.
// 브라우저로 부터 받아오는 정보를 표시할 수 있다.
public Map<String, String> insertMember(@RequestBody Member m) {
Map<String, String> responseData = new HashMap();
//에러가 나면 catch블록
try {
memberService.insertMember(m);
responseData.put("msg", "ok");
} catch (Exception e) {
e.printStackTrace();
responseData.put("msg", e.getMessage());
}
return responseData;
}
// 로그인
@PostMapping("login")
// pm으로 받으면 body 안에 들아간다.
public Map<String, String> login(@RequestBody Member m, HttpServletRequest request) {
System.out.println(m);
Map<String, String> responseData = new HashMap();
// 멤버 서비스에 넘겨서 로그인 해달라고 요청
// MS에서 로그인 메서드 만들어야함 -> 만듬
try {
m= memberService.login(m);
// 어차피 같은 객체이므로 m이라고 함
if(m!=null) { // login ok
// 해당하는 session에 k,v넣어준다.
HttpSession session=request.getSession();
System.out.println(session.getId());
session.setAttribute("member", m);
responseData.put("msg","ok");
} else {
//login fail
responseData.put("msg", "다시 로그인 해주세요");
}
} catch (Exception e) { // login error
// TODO Auto-generated catch block
e.printStackTrace();
responseData.put("msg", "다시 로그인 해주세요");
}
return responseData;
}
}
package com.shop.cafe.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Product;
import com.shop.cafe.service.ProductService;
@RestController
// @CrossOrigin("http://172.30.1.46:8080/")
public class ProductController {
// DB에서 가져오기 위해
@Autowired // new라는 코드를 바꿔서 수행해줌 => DI
ProductService productService;
@GetMapping("getAllProducts") // url 밑에 gap는 메서드 이름 -> 라우팅한다.
public List<Product> getAllProducts(){
try {
return productService.getAllProducts();
} catch (Exception e) {
e.printStackTrace();
// 디버깅을 위해 null
return null;
}
}
}
index.js에서 axios의 credential해제, 요청 url을 상대경로로 바꾸기
springBoot안에 있는 js파일로 가서 // axios.defaults.withCredentials = true; 이렇게 설정해주고 각각의 주소를 지워준다. 파일명을 제외한 나머지 주소들
window.onload = async () => {
// // 세션 저장소 유지되는 코드, 세션에서 이메일 가져와서 로그인 상태 확인하는 코드
const email = sessionStorage.getItem('email');
// 로그인한 유저면 이메일 + 로그아웃 버튼 화면에 표시
if (email) {
document.getElementById('loginSpan').innerHTML =
email + ` <button id="logout">logout</button>`;
}
// axios에서 withCredentials 설정 (쿠키 포함해서 요청 보낼 수 있게)
// axios.defaults.withCredentials = true;
console.log(axios); // axios가 잘 적용됐는지 콘솔에서 확인
// fetch를 하러가는 애를 XMLHttpReguest(XHR)이라고 부른다. forEach로 바로 내려간다. 거기서 기다릴게요가 await이다. await는 async 블록 내에서만 사용 가능하다.
// 서버에서 상품 리스트 가져오기 (GET 요청)
let productList = await fetch('getAllProducts', {
method: 'GET',
});
console.log(productList); // 응답 객체 확인
// productList가 위에서는 text형태이고 밑에서는 배열 형태이다. 배열로 바꾸는 작업 필요
// 응답 데이터를 JSON으로 변환
productList = await productList.json();
console.log(productList); // 변환된 데이터 확인
// 상품 목록 HTML로 만들어서 화면에 추가
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>
<p class="card-text text-danger">${item.price}원</p>
<a href="#" class="btn btn-outline-info" id="addCart">장바구니 담기</a>
</div>
</div>`;
});
// 만든 HTML을 실제 웹 페이지에 삽입
document.getElementById('productListDiv').innerHTML = productListDiv;
};
// 회원가입 버튼 클릭 이벤트
document.getElementById('signupBtn').addEventListener('click', async () => {
// 입력값 가져오기
const nickname = document.getElementById('nickname').value;
const email = document.getElementById('email').value;
const pwd = document.getElementById('pwd').value;
const data = { nickname, email, pwd };
// 서버에 회원가입 요청 보내기
let response = await fetch('insertMember', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
// 응답 데이터를 JSON으로 변환
response = await response.json();
console.log(response); // 응답 확인
// 회원가입 성공하면 모달 닫고, 버튼 제거
if (response.msg === 'ok') {
console.log('ok');
const modal = bootstrap.Modal.getInstance(
document.getElementById('signupModal')
);
modal.hide();
document.getElementById('signupLi').remove();
} else {
alert(response.msg); // 회원가입 실패 메시지
}
});
// 로그인 버튼 클릭 이벤트
document.getElementById('loginBtn').addEventListener('click', async () => {
// 입력값 가져오기
const email = document.getElementById('loginEmail').value;
const pwd = document.getElementById('loginPwd').value;
const data = { email, pwd };
// 서버에 로그인 요청 보내기
let response = await axios.post('login', data);
console.log(response); // 응답 확인
alert(response.data.msg); // 로그인 결과 메시지
// 로그인 성공하면 이메일 표시 + 로그아웃 버튼 추가
if (response.data.msg === 'ok') {
// 로그인창 사라지게
const modal = bootstrap.Modal.getInstance(
document.getElementById('loginModal')
);
modal.hide();
// 화면에 이메일 표시
document.getElementById('loginSpan').innerHTML =
email + ` <button id="logout">logout</button>`;
sessionStorage.setItem('email', email); // 세션에 로그인 정보 저장
}
});
// 상품 목록에서 "장바구니 담기" 버튼 클릭 이벤트
document.getElementById('productListDiv').addEventListener('click', (event) => {
// 클릭한 요소의 id가 "addCart"이면 실행
if (event.target.id == 'addCart') {
axios.post('addCart', {}); // 장바구니에 추가 요청 보내기
}
});
// 로그아웃 버튼 클릭 이벤트
document.getElementById('loginSpan').addEventListener('click', (event) => {
// 로그아웃 버튼을 클릭하면 실행
if (event.target.id == 'logout') {
sessionStorage.removeItem('email'); // 세션에서 이메일 삭제
window.location.reload(); // 새로고침해서 로그아웃 상태 반영
}
});
그러면 이렇게 쿠키가 저장된걸 확인할 수 있다!!
member.js
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 전역설정함, header에 자동으로 token이 들어감
axios.defaults.headers.common['Authorization'] = token; // Authorization 헤더 설정
// 모달 닫기
const modal = bootstrap.Modal.getInstance(
document.getElementById('loginModal')
);
loginModal.setAttribute('aria-hidden', 'true');
modal.hide();
});
// 메소드 블록에 들어가있지 않음. 이 자스가 로딩될때 무조건 수행됨
// 로그인하고 새로고침할때 여전히 로그인해달라고 떠서 그것을 해결하려고 함
// Authorization, nickname 찾아서 그것이 있으면 Authorization과 nickname 안풀리게 보여줌
// 새로고침하면 실행되는 영역
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') {
// axios에 포스트 요청을 보내겠다. 뒤에 아무것도 없음
// axios.defaults.headers.common['Authorization'] = token; 이게 넘어감
await axios.post('http://localhost:8080/logout');
sessionStorage.removeItem('nickname');
sessionStorage.removeItem('Authorization');
axios.defaults.headers.common['Authorization'] = ''; // Authorization 헤더에서 삭제
window.location.reload();
}
});
앞에 번호는 만드는 순서를 의미
1. dto-product.java
package com.shop.cafe.dto;
public class Product {
private String prodname,pimg;
private int prodcode, price;
public Product(String prodname, String pimg, int prodcode, int price) {
setPimg(pimg);
setPrice(price);
setProdcode(prodcode);
setProdname(prodname);
}
public Product() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Product [prodname=" + prodname + ", pimg=" + pimg + ", prodcode=" + prodcode + ", price=" + price + "]";
}
public String getProdname() {
return prodname;
}
public void setProdname(String prodname) {
this.prodname = prodname;
}
public String getPimg() {
return pimg;
}
public void setPimg(String pimg) {
this.pimg = pimg;
}
public int getProdcode() {
return prodcode;
}
public void setProdcode(int prodcode) {
this.prodcode = prodcode;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
2.dao-productDao.java
package com.shop.cafe.dao;
import java.sql.*;
import java.util.*;
import com.shop.cafe.dto.Product;
public class ProductDao {
static public List<Product> getAllProducts() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/ureca?serverTimezone=UTC", "ureca", "ureca");
PreparedStatement stmt = con.prepareStatement("select * from product limit 6");
ResultSet rs = stmt.executeQuery();
List<Product> list = new ArrayList<>();
while (rs.next()) {
int prodocde = rs.getInt("prodocde");
String prodname = rs.getString("prodname");
String pimg = rs.getString("pimg");
int price = rs.getInt("price");
list.add(new Product(prodname, pimg, prodocde, price));
}
return list;
}
public static void main(String[] args) throws Exception {
System.out.println(getAllProducts());
}
}
secu.properties <- 깃에 안올리기!
DB_DRIVER=com.mysql.cj.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/ureca?serverTimezone=UTC
DB_USER=ureca
DB_PW=ureca
cafeshop.application.java
package com.shop.cafe;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
@SpringBootApplication
@PropertySource("classpath:config/secu.properties")
public class CafeShopApplication {
public static void main(String[] args) {
SpringApplication.run(CafeShopApplication.class, args);
}
}
application.properties
spring.application.name=CafeShop
spring.datasource.driver-class-name=${DB_DRIVER}
spring.datasource.url=${DB_URL}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PW}
dao에서 중요정보가리는거 처리
package com.shop.cafe.dao;
import java.sql.*;
import java.util.*;
import org.springframework.beans.factory.annotation.Value;
import com.shop.cafe.dto.Product;
public class ProductDao {
// 중요정보가린거 가져오기
@Value("${spring.datasource.driver-class-name}")
private String DB_DRIVER;
@Value("${spring.datasource.url}")
private String DB_URL;
@Value("${spring.datasource.username}")
private String DB_USER;
@Value("${spring.datasource.password}")
private String DB_PW;
public List<Product> getAllProducts() throws Exception {
Class.forName("DB_DRIVER");
Connection con=DriverManager.getConnection(DB_URL,DB_USER,DB_PW);
PreparedStatement stmt = con.prepareStatement("select * from product limit 6");
ResultSet rs = stmt.executeQuery();
List<Product> list = new ArrayList<>();
while (rs.next()) {
int prodocde = rs.getInt("prodocde");
String prodname = rs.getString("prodname");
String pimg = rs.getString("pimg");
int price = rs.getInt("price");
list.add(new Product(prodname, pimg, prodocde, price));
}
return list;
}
// public static void main(String[] args) throws Exception {
// System.out.println(getAllProducts());
// }
}
프로젝트 위치로 가서 .gitignore로 가서 **/config/*.* 입력하기
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
prodcutSerivce.java파일을 만들어서 productDao에서 데리고 일을 하려고 한다.
그럴려면 ProductDao로 가서 new를 만드는 component를 입력해준다.
ProductDao
package com.shop.cafe.dao;
import java.sql.*;
import java.util.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import com.shop.cafe.dto.Product;
@Repository
public class ProductDao {
// 중요정보가린거 가져오기
@Value("${spring.datasource.driver-class-name}")
private String DB_DRIVER;
@Value("${spring.datasource.url}")
private String DB_URL;
@Value("${spring.datasource.username}")
private String DB_USER;
@Value("${spring.datasource.password}")
private String DB_PW;
public List<Product> getAllProducts() throws Exception {
Class.forName("DB_DRIVER");
Connection con=DriverManager.getConnection(DB_URL,DB_USER,DB_PW);
PreparedStatement stmt = con.prepareStatement("select * from product limit 6");
ResultSet rs = stmt.executeQuery();
List<Product> list = new ArrayList<>();
while (rs.next()) {
int prodocde = rs.getInt("prodocde");
String prodname = rs.getString("prodname");
String pimg = rs.getString("pimg");
int price = rs.getInt("price");
list.add(new Product(prodname, pimg, prodocde, price));
}
return list;
}
// public static void main(String[] args) throws Exception {
// System.out.println(getAllProducts());
// }
}
productService
package com.shop.cafe;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.shop.cafe.dao.ProductDao;
import com.shop.cafe.dto.Product;
@Service // 자신의 성격을 알려주는 서비스 입력
public class ProductService {
@Autowired //같은 메서드 호출하기
ProductDao productDao;
public List<Product> getAllProducts() throws Exception{
return productDao.getAllProducts();
}
}
productDao
stauts 404는 서버 가동 의미
package com.shop.cafe.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dao.ProductDao;
// 컨트롤러여야 url 라우팅 가능
// 컨트롤러를 웹서버 위에서 가동해야한다.
@RestController
public class ProductController {
@GetMapping("getAllProducts")
public String getAllProducts() {
return "ok";
}
}
그 뒤
package com.shop.cafe.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dao.ProductDao;
import com.shop.cafe.dto.Product;
import com.shop.cafe.service.ProductService;
// 컨트롤러여야 url 라우팅 가능
// 컨트롤러를 웹서버 위에서 가동해야한다.
@RestController
public class ProductController {
@Autowired
ProductService productService;
@GetMapping("getAllProducts")
public List<Product> getAllProducts(){
try {
return productService.getAllProducts();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
성능을 잡아먹는 녀석이 dba이다.
sql의 성능을 높혀야한다.
기능 별 세밀화를 해야한다.
그런문제를 해결해보겠다.
그리고 productDao로 가서 System.out.println("ProductDao getAllProducts() 호출됨"); 입력을 해준뒤 창을 실행한다.
package com.shop.cafe.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import com.shop.cafe.dto.Product;
@Repository
public class ProductDao {
@Value("${spring.datasource.driver-class-name}")
private String DB_DRIVER;
@Value("${spring.datasource.url}")
private String DB_URL;
@Value("${spring.datasource.username}")
private String DB_USER;
@Value("${spring.datasource.password}")
private String DB_PW;
public List<Product> getAllProducts() throws Exception {
System.out.println("ProductDao getAllProducts() 호출됨");
Class.forName(DB_DRIVER);
Connection con = DriverManager.getConnection(DB_URL, DB_USER, DB_PW);
PreparedStatement stmt = con.prepareStatement("select * from product limit 6");
ResultSet rs = stmt.executeQuery();
List<Product> list = new ArrayList<>();
while (rs.next()) {
int prodcode = rs.getInt("prodcode");
String prodname = rs.getString("prodname");
String pimg = rs.getString("pimg");
int price = rs.getInt("price");
list.add(new Product(prodname, pimg, prodcode, price));
}
return list;
}
}
성공적으로 출력되는 것을확인할 수 잇다.
package com.shop.cafe.controller;
import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Product;
import com.shop.cafe.service.ProductService;
@RestController
public class ProductController {
@Autowired
ProductService productService;
Map<String, Object> storage = new HashMap<>();
@GetMapping("getAllProducts")
public List<Product> getAllProducts() {
try {
Object o = storage.get("firstPageProducts");
if (o == null) {
List<Product> list = productService.getAllProducts();
storage.put("firstPageProducts", list);
return list;
}
return (List<Product>) o;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
변경된 내용을 return 해준뒤
자스로 가서 출력한다.
index.html에서 axiosd cdn을 설정해준다.
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
자바스크립트로 가서 index.js에서 login 요청을 axios로 변경한다.
자바스크립트의 객체로 파싱을 한뒤 리턴해준다.
window.onload = async () => {
// 자시의 형태로 파싱을 한뒤 리턴해준다.
let response = await axios.get('http://localhost:8080/getAllProducts');
console.log(response);
};
지금은 서버에서 막기에 crossOrigin을 해준
package com.shop.cafe.controller;
import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cafe.dto.Product;
import com.shop.cafe.service.ProductService;
@RestController
@CrossOrigin("http://172.30.1.46:5500/")
public class ProductController {
@Autowired
ProductService productService;
Map<String, Object> storage = new HashMap<>();
@GetMapping("getAllProducts")
public List<Product> getAllProducts() {
try {
Object o = storage.get("firstPageProducts");
if (o == null) {
List<Product> list = productService.getAllProducts();
storage.put("firstPageProducts", list);
return list;
}
return (List<Product>) o;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
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}.png" class="card-img-top" alt="...">
<div class="card-body">
<b class="card-title">${item.prodname}</b>
<p class="card-text text-danger">${item.price}원</p>
<a href="#" class="btn btn-outline-info">장바구니 담기</a>
</div>
</div>`;
});
document.getElementById('productListDiv').innerHTML = productListDiv;
};
이미지는 안뜨나..ㅋㅋ 프론트와 서버와의 연동은 성공한 것이다.
회원가입 만들기
Member
package com.shop.cafe.dto;
import java.util.Date;
public class Member {
private String email, pwd, nickname;
private Date registDate;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getRegistDate() {
return registDate;
}
public void setRegistDate(Date registDate) {
this.registDate = registDate;
}
public Member() {
super();
// TODO Auto-generated constructor stub
}
public Member(String email, String pwd, String nickname, Date registDate) {
super();
this.email = email;
this.pwd = pwd;
this.nickname = nickname;
this.registDate = registDate;
}
@Override
public String toString() {
return "Member [email=" + email + ", pwd=" + pwd + ", nickname=" + nickname + ", registDate=" + registDate
+ "]";
}
}
MemberDao
package com.shop.cafe.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.Statement;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import com.shop.cafe.dto.Member;
@Repository
public class MemberDao {
@Value("${spring.datasource.driver-class-name}")
private String DB_DRIVER;
@Value("${spring.datasource.url}")
private String DB_URL;
@Value("${spring.datasource.username}")
private String DB_USER;
@Value("${spring.datasource.password}")
private String DB_PW;
public void insertMember(Member m) throws Exception {
Class.forName(DB_DRIVER);
String sql="insert into member(nickname, pwd, email) values(?,?,?)";
try(
Connection con=DriverManager.getConnection(DB_URL,DB_USER,DB_PW);
PreparedStatement stmt=con.prepareStatement(sql);
){
stmt.setString(1, m.getNickname());
stmt.setString(2, m.getPwd());
stmt.setString(3, m.getEmail());
int i=stmt.executeUpdate();
System.out.println(i+"행이 insert되었습니다");
}
}
}
MemberService
package com.shop.cafe.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.shop.cafe.dao.MemberDao;
import com.shop.cafe.dto.Member;
@Service
public class MemberService {
@Autowired
MemberDao memberDao;
public void insertMember(Member m) throws Exception {
memberDao.insertMember(m);
}
}
'💡 URECA > 🗒️ 스터디 노트' 카테고리의 다른 글
[URECA] Day 34 (0) | 2025.03.14 |
---|---|
[URECA] day 33 (0) | 2025.03.13 |
[URECA] Day 31 | backend (0) | 2025.03.11 |
[URECA] Day 30 | Backend(5) (0) | 2025.03.10 |
[URECA] Day 29 | Backend(4) 🛍️ (0) | 2025.03.07 |