상세 컨텐츠

본문 제목

[웹 개발 챌린지] 12. 데이터베이스 & 13. Spring-DB 연결

[SW]/웹 개발 (2025)

by 시원00 2025. 2. 6. 19:31

본문

728x90

웹 개발 챌린지 : 나만의 웹 서비스 만들기

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

 

챕터 12. 데이터베이스

12-1. 데이터베이스 개념

데이터베이스란?

일반적인 컴퓨터(하드디스크) 저장은 잘 저장이 되어도 찾을 때(조회) 문제가 됨

-> 데이터베이스: 정보를 빨리 찾을 수 있도록 관리자가 있는 데이터 집합소

 

DBMS(Database Management System)

데이터 베이스 관리 시스템. Oracle, MySQL, MariaDB 등이 있음

(참고) MySQL을 개선한 것이 MariaDB

 

SQL(Structured Query Language)

관계형 데이터 베이스에 사용되는 언어(명령문)

SQL을 통해 추가, 조회, 수정, 삭제가 가능함


12-2. 관계형 데이터베이스 | 데이터베이스, 테이블, 컬럼, 로우데이터

관계형 데이터베이스 중요 개념

1. 데이터베이스-MariaDB(스키마-MySQL): 데이터베이스의 이름(보통 서비스명)

2. 테이블: 서비스에 필요한 항목

3. 컬럼(필드): 테이블의 속성

4. (로우)데이터: 테이블에 들어온 하나의 일렬 데이터

 

예시. 00대학교 (데이터베이스, 스키마)

학생 (테이블)
학번 (컬럼) 이름 (컬럼) 전공 (컬럼) 주소 (컬럼)
20201111 김철수 수학과 부산
20180101 이영희 소프트웨어학과 서울

 

수업 (테이블)
수업코드 (컬럼) 강의명 (컬럼) 담당교수 (컬럼)  
1 수학 이교수 (← 로우데이터)
2 문학 박교수 (← 로우데이터)

 

강의수강 (테이블)
수업코드 (컬럼) 학번 (컬럼)    
1 20201111    
2 20180101    

 

강의수강 테이블은 학생 테이블과 수업 테이블을 JOIN한 것 → 관계 정의

관계형 데이터베이스는 이와 같이 관계 정의에 유리함

 

(참고) NoSQL과 같은 비관계형 데이터베이스는 컬럼이 없음 (학생 테이블에 숫자값 100만 넣어도 됨)


12-3. 인덱스

데이터베이스는 기본적으로 4가지 명령어(SQL 쿼리문)로 구성된다.

SELECT(가져오기), INSERT(추가하기), UPDATE(수정하기), DELETE(삭제하기)

 

이 중 가장 부하가 많이 걸리는 명령어는 원하는 데이터를 찾는 SELECT이다.

따라서 빨리 찾기 위해 (혹은 컬럼의 고유성을 나타내기 위해) 인덱스가 존재한다.

 

데이터 베이스 컬럼에 인덱스를 추가할 수 있다.

 

primary key

테이블에서 가장 주된 고유의 인덱스

테이블에서 하나의 컬럼에만 사용할 수 있는 가장 고유성 및 중심이 되는 인덱스

(12-2. 00대학교 데이터베이스 참고) 학생 테이블에서는 학번, 수업 테이블에서는 수업코드가 primary key가 될 수 있음

 

index

일반적으로 빨리 찾아주는 인덱스

인덱스가 없으면 full-scan을 통해 모든 로우데이터를 조회해야 됨

인덱스를 통해 찾으면 주로 0.5초 내에 찾을 수 있음

모든 컬럼에 인덱스를 부여하면 관리할 것이 더 많아지기 때문에(역효과) 적절히 사용해야 함

 

unique

고유성을 확립하여 중복 데이터 입력을 방지하는 인덱스

unique를 사용하면 아이디, 닉네임 등의 값을 받을 때 중복된 데이터 입력을 방지할 수 있음

(참고) 입력 값이 기존 데이터에 있는지 전체 스캔 후 받는 방법도 많이 사용됨 - unique 사용X


12-4. mariadb & hediSQL 로컬에 구축

로컬에서 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)를 통해 시각화함


12-5. SQL 쿼리문 테스트

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',
	'시원',
	'서울'
)

1번 데이터 추가
2번 데이터 추가

 

위와 같이 Query문을 통해 테이블에 데이터를 넣는 과정 → Spring의 역할 (DB 연동)

 


챕터 13. Spring-DB 연결

13-1. mybatis 개념 및 프로젝트 세팅

Spring과 구축한 데이터베이스를 연동시켜보자

 

Spring + DB 연동 방식에는 크게 2가지가 있다.

1. mybatis 방식: DB 명령문인 SQL 쿼리문을 직접 코딩하여 구축하는 방식

2. JPA 방식: mybatis에서 직접 코딩하던 SQL 쿼리문을 함수로 만들어서 간단하게 구동하는 방식

 

JPA 방식을 사용하더라도 추후 복잡한 쿼리문은 직접 코딩하거나 기본적인 SQL 쿼리를 이해해야 한다.

따라서 처음에는 mybatis 방식으로 SQL 쿼리문을 직접 짜면서 개발해 보는 것이 좋다.

mybatis를 먼저 사용해보면 추후 JPA를 사용할 때 쉽게 적용이 가능할 것이다.

 

mybatis 연동하기

build.gradle

dependencies {
  ...

  //mybatis
  implementation

'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.2'

  //mariadb
  implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.2'
}

 

application.properties

#(선택)mariadb 연결을 위한 주소 및 DB 계정 정보
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://127.0.0.1:3306/my_db?serverTimezone=UTC&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=mariadb비밀번호

#vo에서 상세 패키지 이름 생략(경로 확인)
mybatis.type-aliases-package=com.my.패키지명.vo

#mapper 등록(경로 수정)

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에 만들면 됨)


13-2. 1개 조회하기

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)


13-3. insert

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 사용


13-4. select

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 쿼리문을 연동하는 기능


13-5. select list

만드는 방식 정리

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


13-6. update

회원의 주소 업데이트 하기

 

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


13-7. delete

회원 테이블에서 데이터 삭제하기 (ex. 회원 탈퇴)

 

I. DELETE

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

 

II. UNJOIN

데이터를 삭제하지 않고 관계만 끊거나 비활성화하기

 

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

  • 특정 데이터를 완전히 삭제할 때 사용합니다.
  • 예를 들어, 관계형 데이터베이스에서 DELETE FROM users WHERE id = 1; 하면 해당 사용자의 데이터가 사라집니다.
  • 보통 영구 삭제를 의미하며, 복구가 어렵습니다.

2. UNJOIN

  • UNJOIN은 SQL의 공식적인 명령어가 아니지만, 문맥에 따라 연결을 해제한다는 의미로 사용될 수 있습니다.
  • 예를 들어, 회원 탈퇴 시 실제 데이터를 삭제(DELETE)하지 않고, 관계만 끊거나 플래그(is_active = false같은 것)를 업데이트하는 경우를 의미할 수 있습니다.
  • DELETE 대신 UPDATE를 사용하여 데이터를 보존하면서 비활성화할 수도 있습니다.

 

(참고) UNJOIN을 사용하는 이유 (탈퇴 시 데이터를 보존하는 이유)

1. 법적 및 규제 요구 사항

일부 국가나 지역에서는 법적으로 사용자 데이터를 일정 기간 보관해야 합니다. 예를 들어, 금융 거래 기록이나 계약 관련 정보 등은 규제에 따라 보관해야 할 수 있습니다.

2. 서비스 이력 및 고객 지원

사용자의 서비스 이용 기록을 보관하여 향후 고객 지원 시 참고하거나, 재가입 시 이전 데이터를 복구하는 데 활용할 수 있습니다.

3. 악용 방지 및 보안

사기, 악의적인 활동 등을 추적하고 방지하기 위해 탈퇴한 회원의 기록을 유지할 수 있습니다.

4. 통계 및 분석 목적

서비스 개선을 위해 사용자 이탈률 분석, 사용자 행동 패턴 등을 파악할 때 과거 데이터를 활용할 수 있습니다.

5. 재가입 용이성

사용자가 다시 가입할 경우 이전 데이터를 복구하여 쉽게 서비스를 재이용할 수 있도록 돕습니다.

6. 기술적 이유

데이터베이스 구조나 외래 키 제약 조건 등으로 인해 완전 삭제가 어려울 수 있습니다.

 

 

FIN.

728x90

관련글 더보기

댓글 영역