2025.02.18 JDBC를 MVC로 나누어 공부해봤다.
JDBC란?
JDBC는 자바 프로그램이 데이터베이스와 대화할 수 있게 해주는 통역사 같은 역할을 한다.. 마치 외국인 친구와 대화할 때 통역사가 필요한 것처럼, 자바와 데이터베이스 사이에도 JDBC라는 통역사가 필요하다.
1. JDBC의 주요 구성요소
1) DriverManager
- 데이터베이스 드라이버를 관리하는 매니저 역할이다.
- 마치 택시 배차 센터처럼 적절한 드라이버를 찾아서 연결해준다.
2) Connection
- 데이터베이스와의 실제 연결을 담당한다.
- 마치 전화선처럼 자바 프로그램과 데이터베이스를 이어주는 통로다.
3) Statement/PreparedStatement
- SQL 명령어를 데이터베이스에 전달하는 역할을 한다.
- PreparedStatement는 SQL 인젝션을 방지하는 더 안전한 방법이다.
4) ResultSet
- 데이터베이스에서 가져온 결과를 저장하는 곳이다.
- 엑셀 시트처럼 행과 열로 구성되어 있다.
2. JDBC 사용 기본 단계
- 드라이버 로드하기
- 데이터베이스와 대화하기 위한 준비를 한다.
- 데이터베이스 연결하기
- 데이터베이스 주소, 아이디, 비밀번호를 이용해 연결한다.
- SQL 명령 준비 및 실행
- 원하는 데이터베이스 작업을 수행한다.
- 결과 처리하기
- 데이터베이스에서 받은 결과를 처리한다.
- 연결 닫기
- 사용이 끝난 자원을 정리한다.
3. JDBC 사용시 주의사항
- 자원 관리
- Connection, Statement, ResultSet은 반드시 닫아주어야 한다.
- try-with-resources 구문 사용을 권장한다.
- 예외 처리
- SQL 관련 예외들을 적절히 처리해야 한다.
- 데이터베이스 연결 실패, SQL 문법 오류 등을 고려해야 한다.
- 커넥션 풀 사용
- 많은 연결이 필요할 때는 커넥션 풀을 사용하면 좋다.
- HikariCP 같은 커넥션 풀 라이브러리를 추천한다.
4. 실제 활용 예시 상황
- 회원 관리 시스템
- 회원 정보 저장, 조회, 수정, 삭제
- 쇼핑몰 주문 시스템
- 상품 정보 관리, 주문 처리, 재고 관리
- 게시판 시스템
- 게시글 작성, 조회, 수정, 삭제
소스코드
Client
public class Client {
public static void main(String[] args) {
Controller app = new Controller();
app.startApp();
}
}
Controller
public class Controller {
private BoardDAO boardDAO;
private MemberDAO memberDAO;
private View view;
private MemberDTO user; // 로그인 여부
public Controller() {
this.boardDAO = new BoardDAO();
this.memberDAO = new MemberDAO();
this.view = new View();
this.user = null; // 초기에는 비로그인 상태
}
public void startApp() {
while(true) {
if(user != null) { // 로그인 상태라면
view.printMenuLogin();
}
else {
view.printMenuLogout();
}
int action=view.inputAction();
if(action==1) {
if(user == null) {
continue;
}
String name=view.inputName();
MemberDTO dto=new MemberDTO();
dto.setMember_name(name); // 사용자가 입력한 이름 정보
dto.setMember_id(user.getMember_id()); // 현재 로그인한 아이디 정보
boolean flag=memberDAO.update(dto);
if(flag) {
user=null; // 로그아웃 강제
}
view.printResult(flag);
}
else if(action==2) {
if(user == null) {
continue;
}
BoardDTO dto=new BoardDTO();
dto.setCondition("UPDATE_DELETEMEMBER");
dto.setWriter(user.getMember_id());
boardDAO.update(dto);
boolean flag=memberDAO.delete(user);
if(flag) {
user=null; // 로그아웃 강제
}
view.printResult(flag);
}
else if(action==3) {
if(user == null) {
continue;
}
BoardDTO dto=view.inputBoardDTO();
dto.setWriter(user.getMember_id());
boolean flag=boardDAO.insert(dto);
view.printResult(flag);
}
else if(action==4) {
if(user == null) {
continue;
}
int num = view.inputAction();
BoardDTO dto=new BoardDTO();
dto.setNum(num);
BoardDTO data=boardDAO.selectOne(dto);
view.printData(data);
if(data.getWriter().equals(user.getMember_id())) { // 본인인증
String content=view.inputContent();
dto=new BoardDTO();
dto.setNum(num);
dto.setContent(content);
dto.setCondition("UPDATE");
boolean flag=boardDAO.update(dto);
view.printResult(flag);
}
}
else if(action==5) {
if(user == null) {
continue;
}
int num = view.inputAction();
BoardDTO dto=new BoardDTO();
dto.setNum(num);
boolean flag=boardDAO.delete(dto);
view.printResult(flag);
}
else if(action==6) {
if(user == null) {
continue;
}
user=null;
}
else if(action==7) {
if(user != null) {
continue;
}
MemberDTO memberDTO=view.inputMemberDTO();
boolean flag=memberDAO.insert(memberDTO);
view.printResult(flag);
}
else if(action==8) {
if(user != null) {
continue;
}
MemberDTO memberDTO=view.login();
MemberDTO data=memberDAO.selectOne(memberDTO);
if(data==null) {
view.printResult(false);
}
else {
this.user = data;
this.user.setMember_password(null);
view.printResult(true);
}
}
else if(action==9) {
BoardDTO dto=new BoardDTO();
dto.setCondition("SELECTALL");
view.printDatas(boardDAO.selectAll(dto));
}
else if(action==10) {
action = view.inputAction();
BoardDTO dto=null;
if(action == 1) {
// 작성자 검색
String name = view.inputName();
dto=new BoardDTO();
dto.setCondition("SEARCH_WRITER");
dto.setSearchKeyword(name);
}
else {
// 제목 검색
String title = view.inputTitle();
dto=new BoardDTO();
dto.setCondition("SEARCH_TITLE");
dto.setSearchKeyword(title);
}
ArrayList<BoardDTO> datas=boardDAO.selectAll(dto);
view.printDatas(datas);
}
else {
break;
}
}
}
}
Model
BoardDAO
package javastudy.day026.model.board;
import javastudy.day026.model.common.JDBCUtil;
import java.sql.*;
import java.util.ArrayList;
// 목록출력
// 검색
public class BoardDAO {
final String SELECTALL = "SELECT NUM,TITLE,CONTENT,WRITER,CNT,REGDATE FROM BOARD";
final String SELECTALL_SEARCH_TITLE = "SELECT NUM,TITLE,CONTENT,WRITER,CNT,REGDATE FROM BOARD WHERE TITLE LIKE CONCAT('%',?,'%')";
final String SELECTALL_SEARCH_WRITER = "SELECT NUM,TITLE,CONTENT,WRITER,CNT,REGDATE FROM BOARD WHERE WRITER=?";
//final String SELECTONE = "SELECT NUM,TITLE,CONTENT,WRITER,CNT,REGDATE FROM BOARD WHERE NUM=?";
final String SELECTONE = "SELECT BOARD.NUM,BOARD.TITLE,BOARD.CONTENT,BOARD.WRITER,MEMBER.MEMBER_NAME AS NAME,BOARD.CNT,BOARD.REGDATE FROM BOARD LEFT JOIN MEMBER ON BOARD.WRITER=MEMBER.MEMBER_ID WHERE BOARD.NUM=?";
final String INSERT = "INSERT INTO BOARD (TITLE,CONTENT,WRITER) VALUES(?,?,?)";
final String UPDATE = "UPDATE BOARD SET CONTENT=? WHERE NUM=?";
final String UPDATE_DELETEMEMBER = "UPDATE BOARD SET WRITER='' WHERE WRITER=?";
final String DELETE = "DELETE FROM BOARD WHERE NUM=?";
final String UPDATE_CNT = "UPDATE BOARD SET CNT=CNT+1 WHERE NUM=?";
public ArrayList<BoardDTO> selectAll(BoardDTO dto){
ArrayList<BoardDTO> datas=new ArrayList<BoardDTO>();
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtil.connect();
if(dto.getCondition().equals("SELECTALL")) {
pstmt = conn.prepareStatement(SELECTALL);
}
else if(dto.getCondition().equals("SEARCH_TITLE")) {
pstmt = conn.prepareStatement(SELECTALL_SEARCH_TITLE);
pstmt.setString(1, dto.getSearchKeyword());
}
else if(dto.getCondition().equals("SEARCH_WRITER")) {
pstmt = conn.prepareStatement(SELECTALL_SEARCH_WRITER);
pstmt.setString(1, dto.getSearchKeyword());
}
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
BoardDTO data = new BoardDTO();
data.setNum(rs.getInt("NUM"));
data.setTitle(rs.getString("TITLE"));
data.setContent(rs.getString("CONTENT"));
data.setWriter(rs.getString("WRITER"));
data.setCnt(rs.getInt("CNT"));
data.setRegdate(rs.getDate("REGDATE"));
datas.add(data);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.disconnect(conn, pstmt);
}
return datas;
}
public BoardDTO selectOne(BoardDTO dto){
BoardDTO data = null;
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtil.connect();
// 데이터 read, write
pstmt = conn.prepareStatement(SELECTONE);
pstmt.setInt(1, dto.getNum());
ResultSet rs = pstmt.executeQuery();
if(rs.next()) {
data = new BoardDTO();
data.setNum(rs.getInt("NUM"));
data.setTitle(rs.getString("TITLE"));
data.setContent(rs.getString("CONTENT"));
data.setWriter(rs.getString("WRITER"));
data.setName(rs.getString("NAME"));
data.setCnt(rs.getInt("CNT"));
data.setRegdate(rs.getDate("REGDATE"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.disconnect(conn, pstmt);
}
return data;
}
public boolean insert(BoardDTO dto){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtil.connect();
// 데이터 read, write
pstmt = conn.prepareStatement(INSERT);
pstmt.setString(1, dto.getTitle());
pstmt.setString(2, dto.getContent());
pstmt.setString(3, dto.getWriter());
int result = pstmt.executeUpdate();
if(result <= 0) {
return false;
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
JDBCUtil.disconnect(conn, pstmt);
}
}
public boolean update(BoardDTO dto){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtil.connect();
// 조회수 증가
pstmt = conn.prepareStatement(UPDATE_CNT);
pstmt.setInt(1, dto.getNum());
pstmt.executeUpdate();
if(dto.getCondition().equals("UPDATE")) {
pstmt = conn.prepareStatement(UPDATE);
pstmt.setString(1, dto.getContent());
pstmt.setInt(2, dto.getNum());
}
else if(dto.getCondition().equals("UPDATE_DELETEMEMBER")) {
pstmt = conn.prepareStatement(UPDATE_DELETEMEMBER);
pstmt.setString(1, dto.getWriter());
}
int result = pstmt.executeUpdate();
if(result <= 0) {
return false;
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
JDBCUtil.disconnect(conn, pstmt);
}
}
public boolean delete(BoardDTO dto){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtil.connect();
// 데이터 read, write
pstmt = conn.prepareStatement(DELETE);
pstmt.setInt(1, dto.getNum());
int result = pstmt.executeUpdate();
if(result <= 0) {
return false;
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
JDBCUtil.disconnect(conn, pstmt);
}
}
}
BoardDTO
package javastudy.day026.model.board;
import java.util.Date;
public class BoardDTO {
private int num;
private String title;
private String content;
private String writer; // FK : 상대 테이블의 PK ▶ JOIN으로 데이터 불러오기 가능
private int cnt;
private Date regdate;
private String name; // JOIN으로 받아올 데이터
private String condition;
private String searchKeyword;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCondition() {
return condition;
}
public void setCondition(String condition) {
this.condition = condition;
}
public String getSearchKeyword() {
return searchKeyword;
}
public void setSearchKeyword(String searchKeyword) {
this.searchKeyword = searchKeyword;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
public Date getRegdate() {
return regdate;
}
public void setRegdate(Date regdate) {
this.regdate = regdate;
}
@Override
public String toString() {
return "BoardDTO [num=" + num + ", title=" + title + ", content=" + content + ", writer=" + writer + ", cnt="
+ cnt + ", regdate=" + regdate + "]";
}
}
JDBCUtil
package javastudy.day026.model.common;
import java.sql.*;
public class JDBCUtil {
static final String driverName = "com.mysql.cj.jdbc.Driver";
static final String url = "jdbc:mysql://localhost:3306/test";
static final String userName = "root";
static final String password = "12345678";
public static Connection connect(){
Connection conn = null;
try {
// 1. 드라이버 연결
Class.forName(driverName);
// 2. conn 연결
conn = DriverManager.getConnection(url, userName, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
public static void disconnect(Connection conn, PreparedStatement pstmt){
// 4. DB 연결 해제
try {
pstmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
MemberDAO
package javastudy.day026.model.member;
import javastudy.day026.model.common.JDBCUtil;
import java.sql.*;
import java.util.ArrayList;
public class MemberDAO {
final String SELECTONE = "SELECT * FROM MEMBER WHERE MEMBER_ID=? AND MEMBER_PASSWORD=?";
final String INSERT = "INSERT INTO MEMBER VALUES(?,?,?)";
final String UPDATE = "UPDATE MEMBER SET MEMBER_NAME=? WHERE MEMBER_ID=?";
final String DELETE = "DELETE FROM MEMBER WHERE MEMBER_ID=?";
private ArrayList<MemberDTO> selectAll(MemberDTO dto){
return null;
}
public MemberDTO selectOne(MemberDTO dto){
MemberDTO data = null;
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtil.connect();
// 3. 데이터 read, write
pstmt = conn.prepareStatement(SELECTONE);
pstmt.setString(1, dto.getMember_id());
pstmt.setString(2, dto.getMember_password());
ResultSet rs = pstmt.executeQuery();
if(rs.next()) {
data=new MemberDTO();
data.setMember_id(rs.getString("MEMBER_ID"));
data.setMember_password(rs.getString("MEMBER_PASSWORD"));
data.setMember_name(rs.getString("MEMBER_NAME"));
}
return data;
} catch (Exception e) {
e.printStackTrace();
return data;
} finally {
JDBCUtil.disconnect(conn, pstmt);
}
}
public boolean insert(MemberDTO dto){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtil.connect();
// 3. 데이터 read, write
pstmt = conn.prepareStatement(INSERT);
pstmt.setString(1, dto.getMember_id());
pstmt.setString(2, dto.getMember_password());
pstmt.setString(3, dto.getMember_name());
int result = pstmt.executeUpdate();
if(result <= 0) {
return false;
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
JDBCUtil.disconnect(conn, pstmt);
}
}
public boolean update(MemberDTO dto){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtil.connect();
// 3. 데이터 read, write
pstmt = conn.prepareStatement(UPDATE);
pstmt.setString(1, dto.getMember_name());
pstmt.setString(2, dto.getMember_id());
int result = pstmt.executeUpdate();
if(result <= 0) {
return false;
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
JDBCUtil.disconnect(conn, pstmt);
}
}
public boolean delete(MemberDTO dto){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtil.connect();
// 3. 데이터 read, write
pstmt = conn.prepareStatement(DELETE);
pstmt.setString(1, dto.getMember_id());
int result = pstmt.executeUpdate();
if(result <= 0) {
return false;
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
JDBCUtil.disconnect(conn, pstmt);
}
}
}
MemberDTO
package javastudy.day026.model.member;
public class MemberDTO {
private String member_id;
private String member_password;
private String member_name;
public String getMember_id() {
return member_id;
}
public void setMember_id(String member_id) {
this.member_id = member_id;
}
public String getMember_password() {
return member_password;
}
public void setMember_password(String member_password) {
this.member_password = member_password;
}
public String getMember_name() {
return member_name;
}
public void setMember_name(String member_name) {
this.member_name = member_name;
}
@Override
public String toString() {
return "MemberDTO [member_id=" + member_id + ", member_password=" + member_password + ", member_name="
+ member_name + "]";
}
}
코드 설명
코드의 전반적인 내용은 DB와 연동해서 IDE Console을 이용한 커뮤니티 웹이다. 기능으로는 글 검색, 글 작성, 선택, 수정, 삭제, 회원탈퇴 등 다양하게 이루어진다.
데이터베이스는 MYSQL을 사용했다. JDBC를 통해 연동했는데 중복되는 처리 부분은 JDBCUtil 자바 클래스 파일을 새로 파 정리해두고 모듈화를 시켜 유지보수성을 향상시켰다.
SQL문을 확인해보면 값을 집어넣는 곳은 전부 물음표(?)로 이루어져 있는데 이 부분은 Java에서 코드 작업을 통해 값을 넣어주면 된다. 이렇게 DTO 값을 가져와 쿼리문에 넣어지면 높은 응집도와 낮은 결합도를 유지할 수 있다.
공부하는 차원에서 진행해 UI/UX는 챙기지 못했지만 커뮤니티 웹 기초에 대해 알 수 있는 소중한 경험이였다.
테이블을 팔 때 PK와 FK를 필수적으로 잘 정해두고 정리해둬야 나중에 JDBC를 연동할 때 수월하게 진행될 수 있다.
'Back-End' 카테고리의 다른 글
| [JSP/Servlet] 웹개발 기초 1 (0) | 2025.02.26 |
|---|---|
| [JSP/Servlet] 웹개발 기초 (1) | 2025.02.20 |
| [JAVA] 오답노트 및 발표 피드백 (1) | 2025.02.17 |
| [JAVA] Toy-Project (1) | 2025.02.07 |
| [크롤링] Jsoup과 Selenium (3) | 2025.02.01 |