웹 개발 챌린지 : 나만의 웹 서비스 만들기
https://bootcamp.likelion.net/courses/technology/next_myweb
챕터 12. 데이터베이스
12-1. 데이터베이스 개념
12-2. 관계형 데이터베이스 | 데이터베이스, 테이블, 컬럼, 로우데이터
12-3. 인덱스
12-4. mariadb & hediSQL 로컬에 구축
12-5. SQL 쿼리문 테스트
챕터 13. Spring-DB 연결
13-1. mybatis 개념 및 프로젝트 세팅
13-2. 1개 조회하기
13-3. insert
13-4. select
13-5. select list
13-6. update
13-7. delete
데이터베이스란?
일반적인 컴퓨터(하드디스크) 저장은 잘 저장이 되어도 찾을 때(조회) 문제가 됨
-> 데이터베이스: 정보를 빨리 찾을 수 있도록 관리자가 있는 데이터 집합소
DBMS(Database Management System)
데이터 베이스 관리 시스템. Oracle, MySQL, MariaDB 등이 있음
(참고) MySQL을 개선한 것이 MariaDB
SQL(Structured Query Language)
관계형 데이터 베이스에 사용되는 언어(명령문)
SQL을 통해 추가, 조회, 수정, 삭제가 가능함
관계형 데이터베이스 중요 개념
1. 데이터베이스-MariaDB(스키마-MySQL): 데이터베이스의 이름(보통 서비스명)
2. 테이블: 서비스에 필요한 항목
3. 컬럼(필드): 테이블의 속성
4. (로우)데이터: 테이블에 들어온 하나의 일렬 데이터
예시. 00대학교 (데이터베이스, 스키마)
학생 (테이블) | |||
학번 (컬럼) | 이름 (컬럼) | 전공 (컬럼) | 주소 (컬럼) |
20201111 | 김철수 | 수학과 | 부산 |
20180101 | 이영희 | 소프트웨어학과 | 서울 |
수업 (테이블) | |||
수업코드 (컬럼) | 강의명 (컬럼) | 담당교수 (컬럼) | |
1 | 수학 | 이교수 | (← 로우데이터) |
2 | 문학 | 박교수 | (← 로우데이터) |
강의수강 (테이블) |
|||
수업코드 (컬럼) | 학번 (컬럼) | ||
1 | 20201111 | ||
2 | 20180101 |
강의수강 테이블은 학생 테이블과 수업 테이블을 JOIN한 것 → 관계 정의
관계형 데이터베이스는 이와 같이 관계 정의에 유리함
(참고) NoSQL과 같은 비관계형 데이터베이스는 컬럼이 없음 (학생 테이블에 숫자값 100만 넣어도 됨)
데이터베이스는 기본적으로 4가지 명령어(SQL 쿼리문)로 구성된다.
SELECT(가져오기), INSERT(추가하기), UPDATE(수정하기), DELETE(삭제하기)
이 중 가장 부하가 많이 걸리는 명령어는 원하는 데이터를 찾는 SELECT이다.
따라서 빨리 찾기 위해 (혹은 컬럼의 고유성을 나타내기 위해) 인덱스가 존재한다.
데이터 베이스 컬럼에 인덱스를 추가할 수 있다.
primary key
테이블에서 가장 주된 고유의 인덱스
테이블에서 하나의 컬럼에만 사용할 수 있는 가장 고유성 및 중심이 되는 인덱스
(12-2. 00대학교 데이터베이스 참고) 학생 테이블에서는 학번, 수업 테이블에서는 수업코드가 primary key가 될 수 있음
index
일반적으로 빨리 찾아주는 인덱스
인덱스가 없으면 full-scan을 통해 모든 로우데이터를 조회해야 됨
인덱스를 통해 찾으면 주로 0.5초 내에 찾을 수 있음
모든 컬럼에 인덱스를 부여하면 관리할 것이 더 많아지기 때문에(역효과) 적절히 사용해야 함
unique
고유성을 확립하여 중복 데이터 입력을 방지하는 인덱스
unique를 사용하면 아이디, 닉네임 등의 값을 받을 때 중복된 데이터 입력을 방지할 수 있음
(참고) 입력 값이 기존 데이터에 있는지 전체 스캔 후 받는 방법도 많이 사용됨 - unique 사용X
로컬에서 MariaDB 구축하기
MariaDB를 로컬 컴퓨터에 설치해보자 (https://mariadb.org/)
강의는 window 기준이라 아래 사이트 참고해서 MariaDB 설치함
강의에서 사용하는 HeidiSQL도 mac에서 지원하지 않기 때문에 아래 포스트 따라 DBeaver 설치함
1. 맥에서 MariaDB 설치하기 (https://velog.io/@jthugg/install-mariadb-in-mac-os-arm64)
3번 시작할 때 마리아디비를 종료하라고 하는데, 종료하면 오류가 뜸
ERROR 2002 (HY000): Can't connect to local server through socket '/tmp/mysql.sock' (2)
brew services list를 통해 마리아디비가 실행 중인지 확인
Name Status User File
mariadb none (사용중X)
brew services start mariadb를 통해 마리아디비 다시 실행
==> Successfully started `mariadb` (label: homebrew.mxcl.mariadb)
brew services list 마리아디비 실행 여부 다시 확인
Name Status User File
mariadb started (사용중)
sudo mariadb-secure-installation 이어서 실행
2. DBeaver Community Edition 설치하기 (https://velog.io/@jthugg/install-dbeaver-in-mac-os-arm64)
디비를 핸들링 할 gui 앱 설치 (Apple M2 칩: MacOS for Apple Silicon (dmg) 버전을 설치)
설치 완료
DBMS는 내부적으로 돌아감
작업을 원활하게 하기 위해 HeidiSQL(DBeaver)를 통해 시각화함
mydb를 만들고 회원(user) 테이블 만들어보기
회원 user : 회원 idx(고유 번호), 아이디 id, 비밀번호 pw, 닉네임 nickname, 주소 address, 등록날짜 created_date
(참고) 이미지 컬럼 사용할 때 데이터타입 VARCHAR 사용
SQL 문으로 데이터 삽입하기
SQL문 실행 후 데이터 새로고침하여 추가된 것 확인 가능
INSERT INTO `user`
(
user_code,
id,
pw,
nickname,
address,
created_date
)
VALUES
(
'code1234',
'coding_good',
'1234',
'시원',
'서울'
)
위와 같이 Query문을 통해 테이블에 데이터를 넣는 과정 → Spring의 역할 (DB 연동)
Spring과 구축한 데이터베이스를 연동시켜보자
Spring + DB 연동 방식에는 크게 2가지가 있다.
1. mybatis 방식: DB 명령문인 SQL 쿼리문을 직접 코딩하여 구축하는 방식
2. JPA 방식: mybatis에서 직접 코딩하던 SQL 쿼리문을 함수로 만들어서 간단하게 구동하는 방식
JPA 방식을 사용하더라도 추후 복잡한 쿼리문은 직접 코딩하거나 기본적인 SQL 쿼리를 이해해야 한다.
따라서 처음에는 mybatis 방식으로 SQL 쿼리문을 직접 짜면서 개발해 보는 것이 좋다.
mybatis를 먼저 사용해보면 추후 JPA를 사용할 때 쉽게 적용이 가능할 것이다.
mybatis 연동하기
build.gradle
'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.2'
application.properties
mybatis.mapper-locations=classpath:mapper/*.xml
폴더 및 파일 생성
vo(Value Object), service, dao(Data Access Object), mapper 생성
Controller → Service → Dao(Data Access Object) → Mapper 순으로 로직 진행
Mapper --(SQL)--DB(mariadb)
intelij는 mapper.UserMapper.xml 파일이 반드시 resources에 있어야 함
(참고) https://siwon-swu18.tistory.com/95
(Eclipse는 com.my.likelion_backend에 만들면 됨)
vo(value object)
하나의 로우데이터를 가져오고 싶을 때 어떤 데이터 타입으로 받아올까?
각 컬럼별로 하나씩(int, String, ..) 받아오면 너무 비효율적이다.
한 번에 받아올 수 있도록 class 데이터 타입을 사용하는 것이 가장 효율적이다.
이 때 테이블에 대응되는 class를 vo(value object)라 한다.
vo/User.class
DB테이블에 대응하는 클래스: DB 컬럼의 내용을 다 받아주는 역할(객체)
package com.my.likelion_backend.vo;
public class User {
//private을 사용해서 외부에서 접근 못하게 함 (Default: public)
private int user_idx = 0;
private String user_code = null;
private String id = null;
private String pw = null;
private String nickname = null;
private String address = null;
private String created_date = null;
//받을 수 있는 경우의 수가 너무 많으므로 생성자 함수 사용하지 않음
//get, set 함수 사용 : 우클릭 > Generate > Getter and Setter
public int getUser_idx() {
return user_idx;
}
public void setUser_idx(int user_idx) {
this.user_idx = user_idx;
}
public String getUser_code() {
return user_code;
}
public void setUser_code(String user_code) {
this.user_code = user_code;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPw() {
return pw;
}
public void setPw(String pw) {
this.pw = pw;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCreated_date() {
return created_date;
}
public void setCreated_date(String created_date) {
this.created_date = created_date;
}
}
mapper/UserMapper.xml
직접 DB에 SQL 쿼리문을 수행하여 INSERT, SELECT, UPDATE, DELETE를 수행
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="UserMapper">
<!--DB의 2번 데이터 가져오기-->
<!--resultType="User" : 가져온 값을 vo의 User로 받음-->
<!--application.properties에 경로 지정해놨기 대문에 User(파일명)만 입력하면 됨-->
<select id="findUser2" resultType="User">
<!--쿼리문 작성-->
SELECT * FROM user
WHERE user_idx=2
</select>
</mapper>
dao/UserDao.class
Mapper 함수를 호출하는 부분
@Repository 어노테이션 사용
package com.my.likelion_backend.dao;
import com.my.likelion_backend.vo.User;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository //어노테이션 추가
public class UserDao {
@Autowired //객체 생성 없이 스프링에서 바로 사용할 수 있도록 만들어주는 어노테이션
SqlSession s;
//쿼리문에 바로 넣을 수 있도록 준비가 되어있는 객체
//INSERT, SELECT 등을 할 수 있음
public User findUser2() {
//mapper에서 설정한 resultType((vo.)User)으로 함수 리턴 받기
//User import하기 (vo.USer)
//1. User user = s.selectOne("UserMapper.findUser2"); //어떤 Mapper의 어떤 id를 호출할건지 작성
//Mapper는 java가 아님
//Dao: java 함수로써 Mapper의 쿼리문을 실행시킬 수 있게 해주는 역할
//service에서 dao를 호출할 때 user return
//2. return user;
//1.과 2.를 한 번에 작성
return s.selectOne("UserMapper.findUser2");
}
}
service/UserService.class
Controller의 코드가 길어지면 관리가 힘들어진다.
따라서 비즈니스 로직을 Service에 구성하고 Controller에서 호출하여 사용한다.
Service에서 여러 Dao 함수들을 호출한다.
@Service 어노테이션 사용
package com.my.likelion_backend.service;
import com.my.likelion_backend.dao.UserDao;
import com.my.likelion_backend.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service //추가
public class UserService {
@Autowired
UserDao userDao;
public User findUser2() {
return userDao.findUser2();
}
}
controller/UserController.class
백엔드의 요청을 받아 로직을 수행하는 부분
package com.my.likelion_backend.controller;
import com.my.likelion_backend.service.UserService;
import com.my.likelion_backend.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value="u") // '/u/user2'로 접근
public class UserController {
@Autowired
UserService userService;
@GetMapping("/user2")
@ResponseBody
public User user2() {
User result = userService.findUser2();
return result;
}
}
결과
mydb 확인 (user Data user_idx=2)
detail-user.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h2>회원 상세보기</h2>
<h5>아이디: ${user.id}</h5>
<h5>닉네임: ${user.nickname}</h5>
<h5>주소: ${user.address}</h5>
<h5>가입날짜: ${user.created_date}</h5>
</body>
</html>
UserController.java
package com.my.likelion_backend.controller;
import com.my.likelion_backend.service.UserService;
import com.my.likelion_backend.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value="u") // '/u/user2'로 접근
public class UserController {
@Autowired
UserService userService;
@GetMapping("/user2") //object를 보여줌
@ResponseBody
public User user2() { //ResponseBody: 받아온 객체를 그대로 보여줌
User result = userService.findUser2();
return result;
}
@GetMapping("/user2-page") //뷰(jsp)를 보여줌
public String user2Page(Model model) { //정보를 jsp에 보내야 하므로 Model 사용
User result = userService.findUser2(); //DB에서 정보를 받아 result(class)에 담기
model.addAttribute("user", result); //받아온 정보(result)를 user에 담아 detail-user.jsp에 보내기
return "detail-user"; // jsp 리턴 -> 웹에 보여줌
}
}
/user2와 /user2-page 비교
(좌) http://localhost:8080/likelion_backend/u/user2
(우) http://localhost:8080/likelion_backend/u/user2-page
INSERT
user 테이블에 컨트롤러에서 요청받은 데이터 넣기
기본 문법
INSERT INTO 'table명' (
컬럼1, 컬럼2, ...
)
VALUES (
'값1', '값2', ...
)
VALUES에는 추가할 데이터 입력: 매개변수로 받기
parameterType은 1가지 타입만 넣을 수 있음: vo 클래스 이용
#(필드명)을 통해 클래스의 필드 사용
(참고) SYSDATE(): 현재 날짜 시간 함수
UserMapper.xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="UserMapper">
...
<!--INSERT-->
<insert id="save" parameterType="User"> <!--User 클래스로 input-->
INSERT INTO user
(
user_code,
id,
pw,
nickname,
address,
created_date
)
VALUES
(
#{user_code},
#{id},
#{pw},
#{nickname},
#{address},
SYSDATE()
)
</insert>
</mapper>
UserDao.class
package com.my.likelion_backend.dao;
import com.my.likelion_backend.vo.User;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository //어노테이션 추가
public class UserDao {
@Autowired //객체 생성 없이 스프링에서 바로 사용할 수 있도록 만들어주는 어노테이션
SqlSession s;
//쿼리문에 바로 넣을 수 있도록 준비가 되어있는 객체
//INSERT, SELECT 등을 할 수 있음
...
public int create(User user) { //매개변수로 user를 받아 UserMapper.save의 인자로 집어넣음
return s.insert("UserMapper.save", user); //2번째 인자가 UserMapper.save 함수의 인자로 들어감
//s.insert를 확인해보면 int(영향을 받는 row data의 개수)를 반환하는 것을 알 수 있음
}
}
UserService.class
package com.my.likelion_backend.service;
import com.my.likelion_backend.dao.UserDao;
import com.my.likelion_backend.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service //추가
public class UserService {
@Autowired
UserDao userDao;
...
public int create(User user) {
return userDao.create(user);
}
}
UserController.class
(참고) UUID(Universally Unique Identifier): 128비트의 고유 식별자로 네트워크상에서 중복되지 않는 값을 생성하기 위해 사용
package com.my.likelion_backend.controller;
import com.my.likelion_backend.service.UserService;
import com.my.likelion_backend.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.UUID;
@Controller
@RequestMapping(value="u") // '/u/...'로 접근
public class UserController {
@Autowired
UserService userService;
...
//INSERT
@GetMapping("/create")
@ResponseBody
public String create(
@RequestParam(value="id") String id, //@RequestParam으로 추가할 데이터 받기
@RequestParam(value="pw") String pw,
@RequestParam(value="nn") String nickname,
@RequestParam(value="addr") String address
) {
String code = UUID.randomUUID().toString();
User user = new User();
user.setUser_code(code);
user.setId(id);
user.setPw(pw);
user.setNickname(nickname);
user.setAddress(address);
userService.create(user);
return ("ok");
}
}
결과
http://localhost:8080/likelion_backend/u/create?id=id123&pw=pw123&nn=월레스&addr=런던
실제로는 db에 넣는 과정을 주소창으로 하면 안됨! (사용자가 직접 하면 안됨)
→ JS가 하는 일 (예. '회원가입' 버튼을 누르면 JS로 주소 만듦)
위에서는 주소로 접근해서 확인하기 위해 GET을 사용했지만, 실제로는 POST 사용
Postman 설치 (https://www.postman.com/downloads/)
localhost:8080의 주소창에서는 GET방식(@GetMapping)만 확인할 수 있었음 (POST 방식은 에러)
Postman 앱을 통해 POST 방식도 확인 가능함
@GetMapping을 @PostMapping으로 수정
웹 브라우저에서 주소 입력하면 405 에러 뜸
Postman을 통해 확인 (POST로 설정)
DB에도 추가된 것을 확인할 수 있음
DB(테이블)에서 데이터를 가져오는 방식으로는 크게 2가지가 있다.
1. 1개 가지고 오기 (selectOne)
user_idx에 해당하는 회원정보 가지고 오기
2. 여러 개 가지고 오기 (selectList)
전체 회원 가지고 오기
1개 가지고 오기 (selectOne)
UserMapper.xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="UserMapper">
...
<!--selectOne-->
<select id="findUser" parameterType="Integer" resultType="User">
SELECT * FROM user
WHERE user_idx=#{user_idx}
</select>
</mapper>
UserDao.java
package com.my.likelion_backend.dao;
import com.my.likelion_backend.vo.User;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository //어노테이션 추가
public class UserDao {
@Autowired //객체 생성 없이 스프링에서 바로 사용할 수 있도록 만들어주는 어노테이션
SqlSession s;
//쿼리문에 바로 넣을 수 있도록 준비가 되어있는 객체
//INSERT, SELECT 등을 할 수 있음
...
//selectOne
public User findUser(int user_idx) {
return s.selectOne("UserMapper.findUser", user_idx);
}
}
UserService.java
package com.my.likelion_backend.service;
import com.my.likelion_backend.dao.UserDao;
import com.my.likelion_backend.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service //추가
public class UserService {
@Autowired
UserDao userDao;
...
//selectOne
public User findUser(int user_idx) {
return userDao.findUser(user_idx);
}
}
UserController.java
package com.my.likelion_backend.controller;
import com.my.likelion_backend.service.UserService;
import com.my.likelion_backend.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.UUID;
@Controller
@RequestMapping(value="u") // '/u/...'로 접근
public class UserController {
@Autowired
UserService userService;
...
//selectOne
@GetMapping("/get")
@ResponseBody
public User get(
@RequestParam(value="uidx") int user_idx,
Model model
){
User user = userService.findUser(user_idx);
return user;
}
}
결과
http://localhost:8080/likelion_backend/u/get?uidx=2
view(jsp)로 보기
UserController.java
package com.my.likelion_backend.controller;
import com.my.likelion_backend.service.UserService;
import com.my.likelion_backend.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.UUID;
@Controller
@RequestMapping(value = "u") // '/u/...'로 접근
public class UserController {
@Autowired
UserService userService;
...
@GetMapping("/get") //User Object
@ResponseBody
public User get(
@RequestParam(value = "uidx") int user_idx
) {
User user = userService.findUser(user_idx);
return user;
}
@GetMapping("/getPage") //view(jsp)
public String getPage(
@RequestParam(value="uidx") int user_idx,
Model model
){
User user = userService.findUser(user_idx);
model.addAttribute("user", user);
return "detail-user";
}
}
결과
http://localhost:8080/likelion_backend/u/getPage?uidx=2
백엔드의 핵심 역할
사용자가 입력한 데이터로 SQL 쿼리문을 연동하는 기능
만드는 방식 정리
Mapper → Dao → Service → Controller 순으로 만들기
여러 개 가지고 오기 (selectList)
UserMapper.xml
<!--selectAll-->
<select id="findAll" resultType="User"> <!--parameterType 필요없음(모두 불러오기 때문에)-->
SELECT * FROM user
</select>
UserDao.java
//selectAll - selectList 사용 / return type: List<Object>
public List<User> findAll() {
return s.selectList("UserMapper.findAll");
}
UserService.java
//selectAll
public List<User> findAll() {
return userDao.findAll();
}
UserController.java
@GetMapping("/all")
@ResponseBody
public List<User> all() {
List<User> userList = userService.findAll();
return userList;
}
결과
http://localhost:8080/likelion_backend/u/all
create로 데이터 넣고 all로 확인해보기
selectOne vs. selectList
데이터가 매우 많이 있다고 가정해보고 찾는 데이터가 여러 개일 가능성이 조금이라도 있다면 selectList를 사용한다.
selectOne은 고유값으로 찾을 때 사용한다.
ex. user_idx=3 : selectOne 고유값으로 찾을 때
ex. address='서울' : selecList
회원의 주소 업데이트 하기
update 기본 문법
UPDATE 테이블명
SET
컬럼명=값...
WHERE ...
UserMapper.xml
<!--UPDATE-->
<update id="updateAddress" parameterType="User">
UPDATE user
SET
address=${address}
WHERE <!--모든 회원이 다 바뀌지 않도록 고유값 지정-->
user_idx=${user_idx}
</update>
UserDao.java
//UPDATE
public int updateAddress(User user) {
return s.update("UserMapper.updateAddress", user);
}
UserService.java
//UPDATE
public int updateAddress(User user) {
return userDao.updateAddress(user);
}
UserController.java
//UPDATE
@GetMapping("/chg_addr")
@ResponseBody
public String chgAddr(
@RequestParam(value="user_idx") int user_idx,
@RequestParam(value="new_addr") String new_address
) {
User user = new User();
user.setUser_idx(user_idx);
user.setAddress(new_address);
userService.updateAddress(user);
return "ok";
}
결과
http://localhost:8080/likelion_backend/u/chg_addr?user_idx=1&new_addr='스페인'
(참고) 실제로는 결제 내역 추가, 장바구니 삭제 등 여러 Dao 함수들을 호출해야 함
→ 이것을 관리해 주는 것이 Service
회원 테이블에서 데이터 삭제하기 (ex. 회원 탈퇴)
DB에서 완전 삭제하기
Delete 기본 문법
DELETE FROM 테이블명
WHERE 컬럼명=값
UserMapper.xml
<!--DELETE-->
<delete id="delete" parameterType="Integer">
DELETE FROM user
WHERE user_idx=${user_idx}
</delete>
UserDao.java
//DELETE
public int delete(int user_idx) {
return s.delete("UserMapper.delete", user_idx);
}
UserService.java
//DELETE
public int delete(int user_idx) {
return userDao.delete(user_idx);
}
UserController.java
//DELETE
@PostMapping("/delete")
@ResponseBody
public String delete(
@RequestParam(value = "user_idx") int user_idx
) {
userService.delete(user_idx);
return "ok";
}
결과
http://localhost:8080/likelion_backend/u/delete?user_idx=1
데이터를 삭제하지 않고 관계만 끊거나 비활성화하기
DB - user table
del_ny 컬럼 추가 (default 값: 'n')
User.java
User.java에 del_ny 변수 추가 (getter, setter 모두 추가)
private String del_ny = null;
public String getDel_ny() {
return del_ny;
}
public void setDel_ny(String del_ny) {
this.del_ny = del_ny;
}
UserMapper.xml
<!--updateDelNy-->
<update id="updateDelNy" parameterType="User">
UPDATE user
SET
del_ny=#{del_ny}
WHERE
user_idx=#{user_idx}
</update>
UserDao.java
//updateDelNy
public int updateDelNy(User user) {
return s.update("UserMapper.updateDelNy", user);
}
UserService.java
//updateDelNy
public int updateDelNy(User user) {
return userDao.updateDelNy(user);
}
UserController.java
@PostMapping("unjoin")
@ResponseBody
public String unjoin(
@RequestParam(value="user_idx") int user_idx
) {
User user = new User();
user.setUser_idx(user_idx);
user.setDel_ny("y");
userService.updateDelNy(user);
return "ok";
}
UserDao.xml
findAll(selectAll)에 조건 추가 (WHERE del_ny='n')
<!--selectAll-->
<select id="findAll" resultType="User"> <!--parameterType 필요없음(모두 불러오기 때문에)-->
SELECT * FROM user
WHERE del_ny='n' <!--del_ny가 'n'인 경우만 조회-->
</select>
결과
http://localhost:8080/likelion_backend/u/unjoin?user_idx=3
실제 DB에서 데이터가 삭제되지는 않음 (del_ny만 y로 변경)
전체 회원 조회 시에는 보이지 않음
http://localhost:8080/likelion_backend/u/all
DELETE와 UNJOIN 비교하기
1. DELETE
2. UNJOIN
(참고) UNJOIN을 사용하는 이유 (탈퇴 시 데이터를 보존하는 이유)
1. 법적 및 규제 요구 사항
일부 국가나 지역에서는 법적으로 사용자 데이터를 일정 기간 보관해야 합니다. 예를 들어, 금융 거래 기록이나 계약 관련 정보 등은 규제에 따라 보관해야 할 수 있습니다.
2. 서비스 이력 및 고객 지원
사용자의 서비스 이용 기록을 보관하여 향후 고객 지원 시 참고하거나, 재가입 시 이전 데이터를 복구하는 데 활용할 수 있습니다.
3. 악용 방지 및 보안
사기, 악의적인 활동 등을 추적하고 방지하기 위해 탈퇴한 회원의 기록을 유지할 수 있습니다.
4. 통계 및 분석 목적
서비스 개선을 위해 사용자 이탈률 분석, 사용자 행동 패턴 등을 파악할 때 과거 데이터를 활용할 수 있습니다.
5. 재가입 용이성
사용자가 다시 가입할 경우 이전 데이터를 복구하여 쉽게 서비스를 재이용할 수 있도록 돕습니다.
6. 기술적 이유
데이터베이스 구조나 외래 키 제약 조건 등으로 인해 완전 삭제가 어려울 수 있습니다.
FIN.
[웹 개발 챌린지] 프론트엔드+백엔드 실습 (0) | 2025.02.08 |
---|---|
[웹 개발 챌린지] 프론트엔드 실습 (2) | 2025.02.08 |
[웹 개발 챌린지] 10. JAVA & 11. Controller (1) | 2025.02.01 |
[웹 개발 챌린지] 8. REST API & 9. 실습 (1) | 2025.01.31 |
[웹 개발 챌린지] 6. Javascript & 7. jQuery (0) | 2025.01.30 |
댓글 영역