토이프로젝트 개발일지-2 (feat.ERD구조, DB연동 JDBC)

우진·2023년 6월 23일
1

나의 개발일지

목록 보기
2/3
post-thumbnail

DB 연동을 위해 ERD 구조를 짜고 관계선으로 연결을 해야한다.
하지만, 지금 관계선의 의미를 제대로 파악하지 못하고 있다.
관계선을 어떻게 연결해야하는지 공부중인데 아직도 좀 헷갈린다.

1:1 인지 1:N 인지 1개 이상인지 0개 혹은 1개 뭐 이런 식으로 설명이 되어있는데
초보자가 보기에 무슨 말인지 하나도 모르겠다 ㅋㅋ

내가 이해하지 못한 내용은 내 블로그에 남기고 싶지 않다. 그래서 출처만 남긴다.
나중에 이해했을 때 나만의 방식으로 설명하러 다시 와야겠다.

ERD 관계선에 대해 알고 싶으면 클릭

아래의 툴로 erd 구조를 먼저 짰다.
https://erdcloud.com/

구조를 다 짜고 나면 Export 버튼으로 DDL과 DML도 출력해주니 되게 좋은 툴 같다.

DDL이 뭐야?

DDL(Data Definition Language) : 데이터 정의어
SCHEMA, DOMAIN, TABLE, VIEW, INDEX 를
생성 / 수정 / 삭제 할 때 사용하는 언어.
1. CREATE - 데이터베이스, 테이블 등을 생성
2. ALTER - 테이블 수정
3. DROP - 데이터베이스, 테이블 등을 삭제
4. TRUNCATE - 테이블 초기화

즉, 아래의 문장 같은 것들이 DDL 문이다.

  1. CREATE DATABASE TEST;
  2. DROP TABLE TEST;

DML이 뭐야?

DML(Data Manipulation Language : 데이터 조작어
DB에 데이터를 조회 / 삽입 / 수정 / 삭제 등의 역할
1. SELECT - 데이터 조회
2. INSERT - 데이터 삽입
3. UPDATE - 데이터 수정
4. DELETE - 데이터 삭제

즉, 아래의 문장 같은 것들이 DML 문이다.

  1. SELECT * FROM TEST;
  2. DELETE FROM TEST WHERE 조건;
  3. UPDATE TEST SET NAME = 'woojin', age = 30 WHERE ID = 1;

erdcloud 툴로 만든 테이블


FK를 USER_ID 로 줬는데 찾아보니 현업에서는 FK를 쓰지 않는다는 말도 많이 봤다.
테이블 구조나 수정이 들어가게 되면 결국 설계 자체를 뒤엎어야 하는 경우도 있다는데
그럴 때 FK가 걸려 있으면 이 테이블 저 테이블을 다 같이 한 번에 삭제해야 되기 때문에 훨씬 복잡해진다고 했다.

나는 그렇게까지 스케일이 큰 게 아니라 공부도 할 겸 FK를 줬다.

FK를 뭘로 줄지는 쉬웠다.
문제는 관계선을 뭘로 줘야하는지 찾아보는데만 2시간이 넘게 걸린 거 같다.
아무리 찾아봐도 뭘로 해야할지 감이 잡히지 않아서
결국 ERD 구조를 잘 아는 지인에게 도움을 받고 1:1 실선(identifying relationship)으로 주었다

깨알지식 (DB 기준)
식별관계 - identifying relationship(실선)
비식별관계 - Non-identifying relationship(점선)

참고로 영어 사전에서의 뜻은

  • 실선 - solid line
    점선 - dotted line

이니 헷갈리지 말자.

그저 ERD 구조에서

  • 실선은 identifying relationship
    점선은 Non-identifying relationship
    라고 표현하는 것 뿐이다.
  • 스키마 : 데이터베이스를 의미한다.
  • 실선은 null 값이 있으면 안된다 .
  • 점선은 null 값이 있어도 된다. ( optional )

용어

  • Mandatory : 필수
    NULL 을 허용할거냐 안할거냐의 참고 대상의 존재 여부를 의미한다.

  • Cascade : 폭포란 사전적 뜻을 가지고 있으며 데이터베이스에서는 연쇄작용이 일어나는 것을 가리킨다.

  • Foreign key options
    RESTRICE : 참고 테이블의 값의 변경을 거부한다. ( 일단거절)
    CASCADE : 같이 삭제 또는 수정
    SET NULL : NULL 로 변경
    No ACTION : RESTRICT 랑 동일한 결과 ( 나중에 거절)

  • mysql 은 소문자로 만드는게 관행
    mysql 에 password 명령어가 존재해서 passwd라 한다.
    테이블은 여러개의 데이터가 모여있기 때문에 복수형이 컨벤션이다.

출처 : https://velog.io/@ash3767/DB-ERD-%EB%AA%A8%EB%8D%B8%EB%A7%81

현재 JSP 구조다.

00_Main.jsp 는 이런 구조로 되어있다.
내용이 작아서 보기 좋게 스크린샷으로 찍었다.

01_Buttons.jsp 파일

<%@page import="com.userLogin.UserDTO"%>
<%@page import="com.userLogin.UserDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../css/Main.css">
<%--    <style>
    <%@ include file="/css/Main.css" %>
    </style> --%>
<script src="/js/main.js"></script>
<title>귀농</title>
</head>

<body>
	<%
	request.setCharacterEncoding("utf-8");
	%>
	<%
		UserDTO udto = new UserDTO();
		UserDAO udao = new UserDAO();
		
		udto = udao.myInfo();
	%>
	<div class="container">
		<!-- 왼쪽 버튼 시작 -->
		 <div class="btn-container">
            <button class="left-btns" id="store">상점</button>
            <button class="left-btns" id="bag">가방</button>
            <button class="left-btns" id="chest">창고</button>
            <button class="left-btns" id="farm">농장</button>
            <button class="left-btns" id="map">지도</button>
        </div>
		
		<!-- 위의 div 왼쪽 버튼 끝-->

		<!-- 내정보창 시작 -->
		<div class="info-container">

			<fieldset class="my-info">
				<legend>내 정보</legend>
				<div class="money">
					소지한 돈 :
					<%=udto.getMoney() %>원
				</div>
				<div class="level-container">
					레벨 : <%=udto.getLevel() %>
					<div class="level">(<%=udto.getExp()%>%)</div>
				</div>
				<div class="equipment">
					착용중인 도구 :
					<div class="equipment-name">&nbsp;<%=udto.getEquipment() %></div>
				</div>
				<div class="stats">
					<div class="strong">
						힘 :
						<div class="strong_num">&nbsp;<%=udto.getStr() %></div>
					</div>
					&nbsp;
					<div class="dexterity">
						민첩 :
						<div class="strong_num">&nbsp;<%=udto.getDex() %></div>
					</div>

				</div>
				<div class="playtime">
					<div class="time-text">플레이 시간 :</div>
					<div class="current-time"><%=udto.getPlaytime() %></div>
				</div>
			</fieldset>

		</div>
		
		<!-- 위의 div 내정보창 끝 -->
	</div>
	<!-- 위의 div container 끝 -->

	<!-- middle-container 시작 -->
	<div class="middle-container">
		<!-- 퀘스트 창 시작 -->
		<div class="quest">
			<fieldset class="quest-window">
				<legend>퀘스트 01</legend>

				<div class="quest-title">
					<strong>비료 구매</strong>
				</div>
				<div class="quest-context">
					비료를 구매하자 <br>*상점에서 구매한다.
				</div>
				<div class="quest-package">
					<div class="quest-start">0</div>
					<div class="quest-end">/5</div>
				</div>

			</fieldset>
		</div>

	</div>
	<!-- 위의 div middle-container 끝 -->


</body>

</html>

02_Info.jsp 파일은 아직 없다. 이름도 변경할 거 같다.

자바파일(DTO,DAO)

1. UserDTO.java

package com.userLogin;

public class UserDTO {

	// USER_LOGIN 테이블
	private String user_id;  	// 유저의 아이디
	private String user_pw;		// 유저의 비밀번호
	private String name;		// 유저의 이름
	private String nickName;	// 유저의 닉네임
	private int id; 			// 유저의 인덱스

	// USER_INFO 테이블
	private int money; 			// 현재 보유하고 있는 돈
	private int level; 			// 유저의 현재 레벨
	private double exp; 		// 유저의 현재 경험치
	private String equipment;	// 유저가 현재 장착하고 있는 장비
	private int str; 			// 유저의 현재 힘
	private int dex; 			// 유저의 현재 민첩
	private String playtime;	// 유저의 현재 플레이 시간

	// 생성자
		public UserDTO() {}
		
		public UserDTO(String user_id, String user_pw, String name, String nickName, int id) {
			super();
			this.user_id = user_id;
			this.user_pw = user_pw;
			this.name = name;
			this.nickName = nickName;
			this.id = id;
		}

	public UserDTO(int money, int level, double exp, String equipment, int str, int dex, String playtime) {
		super();
		this.money = money;
		this.level = level;
		this.exp = exp;
		this.equipment = equipment;
		this.str = str;
		this.dex = dex;
		this.playtime = playtime;
	}
	public UserDTO(String user_id, String user_pw, String name, String nickName, int id, int money, int level,
			double exp, String equipment, int str, int dex, String playtime) {
		super();
		this.user_id = user_id;
		this.user_pw = user_pw;
		this.name = name;
		this.nickName = nickName;
		this.id = id;
		this.money = money;
		this.level = level;
		this.exp = exp;
		this.equipment = equipment;
		this.str = str;
		this.dex = dex;
		this.playtime = playtime;
	}
	
	// getter, setter
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
	public int getLevel() {
		return level;
	}
	public void setLevel(int level) {
		this.level = level;
	}
	public double getExp() {
		return exp;
	}
	public void setExp(double exp) {
		this.exp = exp;
	}
	public String getEquipment() {
		return equipment;
	}
	public void setEquipment(String equipment) {
		this.equipment = equipment;
	}
	public int getStr() {
		return str;
	}
	public void setStr(int str) {
		this.str = str;
	}
	public int getDex() {
		return dex;
	}
	public void setDex(int dex) {
		this.dex = dex;
	}
	public String getPlaytime() {
		return playtime;
	}
	public void setPlaytime(String playtime) {
		this.playtime = playtime;
	}

	public String getUser_id() {
		return user_id;
	}
	public void setUser_id(String user_id) {
		this.user_id = user_id;
	}
	public String getUser_pw() {
		return user_pw;
	}
	public void setUser_pw(String user_pw) {
		this.user_pw = user_pw;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getNickName() {
		return nickName;
	}
	public void setNickName(String nickName) {
		this.nickName = nickName;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	
	@Override
	public String toString() {
		return "UserDTO [user_id=" + user_id + ", user_pw=" + user_pw + ", name=" + name + ", nickName=" + nickName
				+ ", id=" + id + ", money=" + money + ", level=" + level + ", exp=" + exp + ", equipment=" + equipment
				+ ", str=" + str + ", dex=" + dex + ", playtime=" + playtime + "]";
	}
	
	
}

2.UserDAO.java

package com.userLogin;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

public class UserDAO {

	public static UserDAO instance = new UserDAO();
//	public String realpath = "";
//	String filename = "/user.txt";

	public static UserDAO getInstance() {
		return instance;
	}

	Connection conn = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;

	// 데이터를 저장해서 활용
	ArrayList<UserDTO> userList = new ArrayList<>();

	// 데이터베이스에 연결하는 내용을 작성
	// mysql에 연결하는 함수를 작성
	public Connection getConnection() {

		// 데이터 베이스에 연결하는 내용을 작성

		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
			String url = "jdbc:mysql://localhost:3306/FARM?serverTimezone=UTC";
			String user = "root";
			String pass = "dldnwls1";

			// 데이터 베이스를 실제 연결을 도와주는 클래스
			conn = DriverManager.getConnection(url, user, pass);

			if (conn != null) {
				System.out.println("DB 연결 성공");
			}

		} catch (ClassNotFoundException e) {
			System.out.println("드라이버 로딩 에러");
			e.printStackTrace();

		} catch (SQLException e) {
			System.out.println("연결 에러");
			e.printStackTrace();
		}

		return conn;
	}

	public UserDTO myInfo() {
		UserDTO udto = null;

		// sql에서 조회해서 내용 가지고 오고 udto 변수에 저장하기.
		try {
			conn = getConnection();

			String sql = "SELECT * FROM USER_INFO";
			pstmt = conn.prepareStatement(sql);

			// 데이터 베이스에서 결과값 꺼내서 객체 만들어주기.
			rs = pstmt.executeQuery();

			// 한 행꺼내온다. 만약 없으면 null
			// 데이터가 없으면 false 있으면 true
			if (rs.next()) {
				udto = new UserDTO();

				udto.setMoney(rs.getInt("money"));
				udto.setLevel(rs.getInt("level"));
				udto.setEquipment(rs.getString("equipment"));
				udto.setStr(rs.getInt("str"));	
				udto.setDex(rs.getInt("dex"));
				udto.setPlaytime(rs.getString("playtime"));
			}

		} catch (Exception e) {
			System.out.println(e.toString());
		} finally {
			if (rs != null) {
				try {
					rs.close();
					conn.close();
					pstmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		return udto;

	}
}

MySQL 데이터

DB연동 전 화면

DB연동 후 화면

첫 번째 목표.

~1. MySQL에 상점 테이블과 유저 테이블을 만든다~

2. 테이블들을 연동해서 유저가 상점에 있는 아이템을 구매하면 유저가 소유하고 있는 돈이 줄어든다.

DB 테이블을 생성해서 1번은 완료했다.
그 테이블의 데이터를 JSP 화면에 출력하는 것까지 성공했다.
아직까지는 순조롭게 진행 되는 것 같다. 기분이 조으다 ㅎㅎ

이제 남은 것은 2번 !

오늘의 생각

"기획할 때는 이렇게 이렇게 메소드와 기능을 넣으면 되겠다." 라고 간단하게 생각했는데,
막상 하려니까 뭐부터 해야할지도 모르겠고 막상 하나 정하고 진행하는데도 순탄치 않았다.
그런데 JDBC 연동으로 데이터 값을 성공적으로 불러오니까 내가 예상했던 그대로 된 느낌이라
기분이 되게 좋았다. 내가 생각하기로 데이터는 불러왔으니까 상점에서 500원 짜리를
구매하면 user_info 테이블에서 money 컬럼에서 user_id 값을 비교한 후에 일치하면
money 에서 -500을 하면 되겠지 라는 간단한 생각을 하고 있지만 또 막상 해보면
얼마나 많은 예외상황들과 엮여있는 것들이 있을지 참 기대되고 기대된다. 재밌다 코딩!!

결론 : DB 연동 성공!

profile
지니입니다

2개의 댓글

comment-user-thumbnail
2023년 6월 24일

한 자 한 자 공들여 코딩한게 느껴지네요,,하루만에 이렇게 성장하다니 멋져요 :)

1개의 답글