MVC 기반의 컴포넌트 JTable : 회원가입폼 (1)

heeezni·2025년 5월 28일
post-thumbnail

MVC(Model-View-Controller)

Model-View-Controller
대규모 프로그램이나 유지보수가 필요한 애플리케이션에서 주로 사용하는 설계 원칙(디자인 패턴)

목적

“데이터 처리(로직)와 화면표시(View)를 분리시켜라”

  • 유지보수 편하게
  • 기능별 분업 가능하게
  • 재사용성 높이기 위해

핵심개념

구성 요소역할설명
Model데이터, 로직 처리DB 처리, 계산 등 실제 데이터를 다룸 (데이터기능)
View사용자 UI사용자에게 보여지는 부분 (HTML, 화면, 디자인 등)
Controller중간 제어자사용자의 입력을 받아서 Model과 View 사이를 연결함

JTable의 MVC패턴 적용

JTable table = new JTable(데이터처리객체: TableModel);
  • TableModel : 데이터 처리 역할 (Model) 담당
  • JTable : 사용자에게 보여주는 View + 이벤트 처리 컨트롤러 역할

JTable은 MVC를 부분적으로 반영한 컴포넌트
➡ 데이터(Model)와 UI(View + Controller)가 어느 정도 분리돼 있음


Spring Tool Suite(STS)

  • Spring Framework 전용 IDE
    (Eclipse 기반의 통합 개발 환경)

  • Spring 개발을 위한 도구와 설정이 사전 탑재된 도구


회원가입폼

JTable은 MVC 패턴 기반의 컴포넌트

  • Model : 데이터를 저장하고 관리 (여기선 MyModel)
  • View + Controller : JTable이 동시에 수행
    (그래서 불완전한 MVC 구조라고 봄)

JTable은 단순히 UI이고, 실제 데이터를 다루는 것은 TableModel

핵심: 데이터(Model)와 테이블(JTable)을 분리하여 유지보수가 쉬운 구조로 만듦

MemberRegist

import java.awt.*;
import java.sql.*;
import javax.swing.*;

public class MemberRegist extends JFrame implements ActionListener, WindowListener {
	// 서쪽 입력 폼 구성 요소
	JPanel p_west;
	JTextField t_id, t_name, t_tel;
	JButton bt;

	// 센터 테이블 구성 요소
	JPanel p_center;
	JTable table;
	JScrollPane scroll;
	TableModel model;

	Connection con = null; // DB 연결 객체
    /*접속은 윈도우 창 생성 시 한 번 시도 후,
    닫을 때 접속해제해야하므로 멤버변수로 빼기*/

	public MemberRegist() {
		// GUI 요소 생성
		p_west = new JPanel();
		t_id = new JTextField();
		t_name = new JTextField();
		t_tel = new JTextField();
		bt = new JButton("가입");

		p_center = new JPanel();
		table = new JTable(model = new MyModel()); 
        // ⭐JTable은 모델 기반으로 데이터를 표현⭐
		scroll = new JScrollPane(table);

		// 스타일 설정
		p_west.setBackground(Color.ORANGE);
		p_west.setPreferredSize(new Dimension(150, 500));

		Dimension d = new Dimension(146, 35);
		t_id.setPreferredSize(d);
		t_name.setPreferredSize(d);
		t_tel.setPreferredSize(d);
		scroll.setPreferredSize(new Dimension(420, 490));

		bt.addActionListener(this); // 버튼 이벤트
		this.addWindowListener(this); // 창 이벤트

		// 컴포넌트 조립
		p_west.add(t_id);
		p_west.add(t_name);
		p_west.add(t_tel);
		p_west.add(bt);
		add(p_west, BorderLayout.WEST);

		p_center.add(scroll);
		add(p_center);

		// 창 설정
		setBounds(100, 300, 600, 500);
		setVisible(true);

		// ✅ DB 연결 및 초기 데이터 조회
		connect(); 
		selectAll();
	}

프로그램이 실행될 때(윈도우창 띄우자마자)
바로 DB 연결하고 데이터를 불러오려면 UI구성 끝나고 connect(), selectAll() 호출

	public void connect() {
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
			String url = "jdbc:mysql://localhost:3306/dev";
			String id = "java";
			String pwd = "****";
			con = DriverManager.getConnection(url, id, pwd);

			if (con != null) setTitle("접속성공");
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
		}
	}
use dev;

create table member4(
member4_id int primary key auto_increment,
id varchar(20),
name varchar(16),
tel varchar(20),
regdate timestamp default now()
);

INSERT하기 전, member4 테이블 생성하기

	public void regist() {
		String sql = "INSERT INTO member4(id, name, tel) VALUES('"
			+ t_id.getText() + "', '" + t_name.getText() + "', '" + t_tel.getText() + "')";

		PreparedStatement pstmt = null;

		try {
			pstmt = con.prepareStatement(sql);
			int result = pstmt.executeUpdate();
            // ✅ executeUpdate : DML(insert, update, delete)수행

			if (result > 0) {
				JOptionPane.showMessageDialog(this, "등록 성공");
                // GUI 팝업창
				selectAll();
			} else {
				JOptionPane.showMessageDialog(this, "등록 실패");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (pstmt != null) pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

⭐⭐⭐주목할 코드⭐⭐⭐

	public void selectAll() {
		String sql = "SELECT * FROM member4";
		PreparedStatement pstmt = null; // finally에서 닫아야해서 지역변수로 빼기
		ResultSet rs = null;

		try {
			pstmt = con.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            // ✅ SELECT문이니까 executeQuery사용
			rs = pstmt.executeQuery();

			rs.last(); // 마지막 행으로 이동
			int total = rs.getRow(); // 전체 행 수
			((MyModel) model).rows = new String[total][3]; // 모델의 데이터 공간 재구성

			rs.beforeFirst(); // 처음 위치로 이동

			int index = 0;
			while (rs.next()) {
				String[] record = {
					rs.getString("id"),
					rs.getString("name"),
					rs.getString("tel")
				};
				((MyModel) model).rows[index++] = record;
			}
			table.updateUI(); // 테이블 새로고침

		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null) rs.close();
				if (pstmt != null) pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) {
		new MemberRegist();
	}
    
    // Button 이벤트 메서드 (필수 구현)
    @Override
	public void actionPerformed(ActionEvent e) {
		regist(); // 가입 버튼 클릭 시 회원 등록
	}

	// Window 이벤트 메서드 (필수 구현)
	@Override public void windowOpened(WindowEvent e) { }
   	/*윈도우 창을 닫는 순간 호출되는 메서드
	연결되어있던 자원을 해제하는 용도로 적합*/
	@Override public void windowClosing(WindowEvent e) {
		System.out.println("windowClosing()");
		try {
			if (con != null) con.close(); // DB 연결 해제
		} catch (SQLException ex) {
			ex.printStackTrace();
		}
	}
	@Override public void windowClosed(WindowEvent e) { }
	@Override public void windowIconified(WindowEvent e) { }
	@Override public void windowDeiconified(WindowEvent e) { }
	@Override public void windowActivated(WindowEvent e) { }
	@Override public void windowDeactivated(WindowEvent e) { }
}

MyModel

JTable에 표시할 데이터를 관리하기 위해 AbstractTableModel을 상속해서 만든 사용자 정의 모델 클래스

import javax.swing.table.AbstractTableModel;

public class MyModel extends AbstractTableModel {
	
	// 테이블에 보여줄 데이터 (2차원 배열: 행 x 열)
	String[][] rows = new String[0][3];

	// 테이블 컬럼 이름들
	String[] columns = { "ID", "Name", "Tel" };

	// 행 개수 반환 (레코드 수)
	@Override
	public int getRowCount() {
		return rows.length;
	}

	// 열 개수 반환 (컬럼 수)
	@Override
	public int getColumnCount() {
	    return columns.length;
	}

	// 각 컬럼의 이름 반환
	@Override
	public String getColumnName(int col) {
		return columns[col];
	}

	// 각 셀의 값 반환 (표시할 데이터)
	@Override
	public Object getValueAt(int row, int col) {
		return rows[row][col];
	}

	// 셀 수정 가능 여부 (기본적으로 true로 설정)
	@Override
	public boolean isCellEditable(int row, int col) {
		System.out.println(row + "행, " + col + "열 수정 가능");
		return true;
	}
}
profile
아이들의 가능성을 믿었던 마음 그대로, 이제는 나의 가능성을 믿고 나아가는 중입니다.🌱

0개의 댓글