공부의 기록/자바 풀 스택 : 수업내용정리

자바 풀 스택 1/2 오전 기록 029-1

파티피플지선 2025. 1. 2. 13:19

9:15 경 학원 도착

 
 
 
웹 프로그래밍에 대한 진도를 나간대서 수업 시작 전까지 저번에 받은 이 교재를 대충 살펴보기로 함.
오늘 뭐지 기분이 걩장히 하이해~ ㅋㅋㅋㅋㅋㅋㅋ
커피 탓인가!
커피 좋아 커피 최고~
밤 한시까지 오버워치하고 3시까지 다른겜했는데 그것도 있으려나 ㅋㅋㅋ
오히려 각성 상태
 
 
 
<9:30 1교시>
오전에는 오늘의 깃허브 ~ fork 해서 clone 하기~!!
fork 하는 이유 (1교시~2교시 중반부까지 이어진 내용)
1. push 권한을 부여할 필요가 없이 한 사람이 일방적으로 merge하는 방식으로 사용하기 위해서 
    포크해온 리포지토리는 그 이후 업데이트 되는 커밋 내용을 동기화하진 못한다.
    포크해온 것을 내 컴퓨터에서 클론해둔 다음, 
    포크 기원에 새로운 커밋 내용이 추가되었을 때는
    내 컴퓨터의 클론된 리포지토리에서 포크해온 기원 리포지토리를 upstream으로 연결하여 내용을 내려 받고
    내 컴퓨터에서 내 깃허브로 올려서 동기화 시키는 모양새이다.
    내가 직접 기원의 마스터 브랜치에 동기화시키지 않는다.
 

fork 하게되면 pull 받는 곳과 푸쉬하는 곳이 달라짐
 
2. 나 혼자 가지고 놀기 목적
 
깃허브 말고 비트버킷이라는 원격저장소도 있다.
 
원격 저장소를 등록하는 방법
 
git   remote   add    원격저장소의이름   원격저장소의위치
우리가 자주 사용하던 명령어처럼 upstream 원격저장소를 등록해주면 된다.
git   remote   add    origin   https://xxxxx/... .git
git   remote   add    upstream    https://xxxx/xxx... .git

원격 저장소가 클론된 폴더 안에서 upstream으로 등록해야 한다!
깨달음!

연결된 원격 저장소가 2개가 됨.
 
 
이후 업스트림 리포지토리가 커밋한걸 내려받고 싶으면
git pull upstream master
이걸 내 깃허브에 올리고 싶으면
git push origin master

 
 
 

 
내가 별도의 작업하고 싶으면 이 브랜치로 체크아웃해서 작업하면 됨

git 브랜치 이름 짓기
git   branch   feature/기능의이름
 
 
가지에서 작업한걸 깃허브에 올리고 싶으면 아래 명령어
git    push    origin    올릴가지의이름   

 
 
 
 
<10:30 2교시>
조장의 주도로 머지되는 팀 작업을 하게 된다면 내가 origin이던 upstream이던 마스터 브랜치에 push 하면 절대 안된다.
upstream에게 compare&pull request(내거좀 봐주세요~ 하는 거, pr 한다고도 함)하면 조장이 보고 pull 여부를 결정.
commit merge를 하거나 close pull request로 거절해버릴 수 있다.
조장은 조원이 pull request를 했다가 닫았다면 reopen 할수도 있다. (조원의 pull request를 했다가 닫았을 때 close pull request로 완전히 닫을 수 있나보다)

내가 만든 가지에 대해 풀 리퀘스트가 완료되면 그 머지된 브랜치는 지워야 한다. (그리고 이렇게 지워버려도 되기 때문에 마스터 브랜치에 push 하는거 절대 금지)

다른 사람이 한걸 upstream에서 머지한 상태라면 내가 내려받아서 새로 작업하면 된다.

 
내가 pr 요청한 게 마무리되면 아래와 같은 화면이 뜬다. 이때 branch들 다 삭제해주면 된다.

git에서는 내가 push 한 feature/crystal 브랜치가 풀 리퀘스트 이후 delete 해서 삭제되었지만, 내 컴퓨터에서는 여전히 feature/crystal 브랜치가 남아 있다. 이것도 지워주는 게 좋다고 한다.
 
지우는 명령어
git   branch   -D   지우려는브랜치이름

 
조장이 일괄 한다는 점에서 merge 문제는 최소화 되지만 어쨋든 조장이 책임지고 판단하고 해야 함.
 
 
어제 하던 자바 수정 기능 넣기 중간 과정 코드 백업

package test.frame;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;

import test.dao.MemberDao;
import test.dto.MemberDto;

public class MemberFrame extends JFrame implements ActionListener, PropertyChangeListener{
	//필요한 필드 정의하기
	JTextField inputName, inputAddr;
	DefaultTableModel model;
	JTable table;
	//생성자
	public MemberFrame(String title) {
		super(title);
		//레이아웃 설정
		setLayout(new BorderLayout());
		//JLabel 2개
		JLabel label2= new JLabel("이름");
		JLabel label3= new JLabel("주소");
		//JTextField 1개
		inputName = new JTextField(10);
		inputAddr = new JTextField(10);
		//JButton
		JButton addBtn=new JButton("추가");
		JButton deleteBtn=new JButton("삭제");
		//패널에 UI 배치(add는 패널의 메소드)
		JPanel panel =new JPanel();
		panel.add(label2);
		panel.add(inputName);
		panel.add(label3);
		panel.add(inputAddr);
		panel.add(addBtn);
		panel.add(deleteBtn);
		//패널의 배경색 설정
		panel.setBackground(Color.orange);
		//패널을 프레임의 위쪽에 배치
		add(panel, BorderLayout.NORTH);
		
		//버튼에 액션 리스너 등록
		addBtn.addActionListener(this);
		deleteBtn.addActionListener(this);
		//버튼에 액션 command 설정
		addBtn.setActionCommand("add");
		deleteBtn.setActionCommand("delete");
		
		//회원 목록을 출력할 테이블-> 필드로 뽑는다 ->여기 있던 JTable table에 대해 앞의 JTable을 지워준다
		table=new JTable();
		//테이블의 칼럼명을 배열로 미리 준비한다
		String[] colNames= {"번호", "이름", "주소"};
		//테이블에 연결할 모델 객체
		model =new DefaultTableModel() {
			@Override
			public boolean isCellEditable(int row, int column) {
				if(column==0) {
					return false;
				}else {
					return true;
				}//return true는 모두 수정가능, return false는 모두 수정불가능
			}
		};//이름이 없지만 DefaultTableModel 클래스를 상속 받은 익명 클래스로 객체 생성하기(안에서 오버라이드 필요하면 오버라이드)
		
		//생성자에서 생성된 이 객체의 참조값이 다른 메소드에서도 필요한 상황 -> 이 값을 지역 변수가 아닌 필드로 옮기기
		model.setColumnIdentifiers(colNames);
		model.setRowCount(0);
		
		//모델을 테이블에 연결
		table.setModel(model);
		//스크롤이 가능하도록 테이블을 JScrollPane에 감싼다
		JScrollPane scroll=new JScrollPane(table);
		//JScrollPnae을 프레임의 가운데에 배치하기
		add(scroll, BorderLayout.CENTER);
		
		//프레임 객체가 생성되는 시점에 DB에 있는 내용을 읽어와서 출력할 수 있게 해준다.
		//JTable에 출력할 sample row 데이터
		//Object[] row= {1, "글린다", "오즈"};
		//테이블에 연결된 모델에 row 추가하기
		//model.addRow(row);
	
		
		printMember();
		//테이블에 출력할 회원 목록 얻어오기
		//MemberDao dao = new MemberDao();
		//List<MemberDto> list= dao.getList();
		//반복문 돌면서
		//for (MemberDto tmp:list ) {
			//MemberDto 객체에 담긴 회원정보를 이용해서 Object[] 에 담은 다음
			//Object[] rowData = {tmp.getNum(), tmp.getName(), tmp.getAddr()};
			//테이블에 연결된 모델에 추가하기
			//model.addRow(rowData);
		//}
		
		
		//칼럼의 제목 글자 조정
		table.getTableHeader().setFont(new Font("Sans-serif", Font.BOLD, 18));
		table.setFont(new Font("Sans-serif", Font.PLAIN,16));//데이터 글자 크기 14
		table.setRowHeight(25);//각 행의 높이를 조정
		
		
		//테이블에 값이 바뀌었는지 감시할 리스너 등록
		table.addPropertyChangeListener(this);
		
		
	}//생성자 : 프레임 객체가 생성되는 시점에 만들어짐 
	
	public static void main(String[] args) {
		MemberFrame f=new MemberFrame("회원 정보 관리");
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//가독성 좋게+부모자식이라 JFrame.생략하고 
		f.setBounds(100,100,800,500);
		f.setVisible(true);
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		//눌러진 버튼의 action command를 읽어온다
		String command=e.getActionCommand();
		if(command.equals("add")) {//java 에서는 문자열 비교를 .equals()메소드를 이용해서 비교해야 한다.  (자바스크립트에서는 command =="add")
			//입력한 이름과 주소를 읽어 온다
			String name=inputName.getText();//this. 이 생략되어 있는데 JTextField의 참조값이 필드에 저장되어 있어서 생략됨
			String addr=inputAddr.getText();//this. 이 생략되어 있는데 JTextField의 참조값이 필드에 저장되어 있어서 생략됨
			//입력한 이름과 주소를 MEmebrDto 객체에 담는다
			MemberDto dto=new MemberDto();
			dto.setName(name);
			dto.setAddr(addr);
			//MemberDao 객체를 생성해서
			MemberDao dao=new MemberDao();
			//insert() 메소드를 이용해서 회원 정보를 DB에 저장한다
			dao.insert(dto);
			
			
		}else if(command.equals("delete")) {
			int selectedRow=table.getSelectedRow();
			if(selectedRow==-1) {
				JOptionPane.showMessageDialog(this, "삭제할 row를 선택하세요");//자바 frame 창에서 알림을 띄우기 this는 알림을 띄울 컴포넌트(멤버프레임)의 참조값, 알림 메시지를 전달.
				return;// 메소드를 여기서 끝내기
			}
			int num= (int)model.getValueAt(selectedRow, 0);
			new MemberDao().delete(num);
		}
		
		printMember();

		
	}
	
	//회원 목록을 JTable에 출력하는 메소드
	public void printMember() {
		//기존에 출력된 내용을 초기화한 후에
		model.setRowCount(0);
		
		//테이블에 출력할 회원 목록 얻어오기
		MemberDao dao = new MemberDao();
		List<MemberDto> list= dao.getList();
		//반복문 돌면서
		for (MemberDto tmp:list ) {
			//MemberDto 객체에 담긴 회원정보를 이용해서 Object[] 에 담은 다음
			Object[] rowData = {tmp.getNum(), tmp.getName(), tmp.getAddr()};
			//테이블에 연결된 모델에 추가하기 -> 모델을 필드로 만들기
			model.addRow(rowData);
		}
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		
		
	}
	
}

 
 
 
 
 
 
 
<11:30 3교시>
와 레알 수정됐다.

 

package test.frame;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;

import test.dao.MemberDao;
import test.dto.MemberDto;

public class MemberFrame extends JFrame implements ActionListener, PropertyChangeListener{
	//필요한 필드 정의하기
	JTextField inputName, inputAddr;
	DefaultTableModel model;
	JTable table;
	//생성자
	public MemberFrame(String title) {
		super(title);
		//레이아웃 설정
		setLayout(new BorderLayout());
		//JLabel 2개
		JLabel label2= new JLabel("이름");
		JLabel label3= new JLabel("주소");
		//JTextField 1개
		inputName = new JTextField(10);
		inputAddr = new JTextField(10);
		//JButton
		JButton addBtn=new JButton("추가");
		JButton deleteBtn=new JButton("삭제");
		//패널에 UI 배치(add는 패널의 메소드)
		JPanel panel =new JPanel();
		panel.add(label2);
		panel.add(inputName);
		panel.add(label3);
		panel.add(inputAddr);
		panel.add(addBtn);
		panel.add(deleteBtn);
		//패널의 배경색 설정
		panel.setBackground(Color.orange);
		//패널을 프레임의 위쪽에 배치
		add(panel, BorderLayout.NORTH);
		
		//버튼에 액션 리스너 등록
		addBtn.addActionListener(this);
		deleteBtn.addActionListener(this);
		//버튼에 액션 command 설정
		addBtn.setActionCommand("add");
		deleteBtn.setActionCommand("delete");
		
		//회원 목록을 출력할 테이블-> 필드로 뽑는다 ->여기 있던 JTable table에 대해 앞의 JTable을 지워준다
		table=new JTable();
		//테이블의 칼럼명을 배열로 미리 준비한다
		String[] colNames= {"번호", "이름", "주소"};
		//테이블에 연결할 모델 객체
		model =new DefaultTableModel() {
			@Override
			public boolean isCellEditable(int row, int column) {
				if(column==0) {
					return false;
				}else {
					return true;
				}//return true는 모두 수정가능, return false는 모두 수정불가능
			}
		};//이름이 없지만 DefaultTableModel 클래스를 상속 받은 익명 클래스로 객체 생성하기(안에서 오버라이드 필요하면 오버라이드)
		
		//생성자에서 생성된 이 객체의 참조값이 다른 메소드에서도 필요한 상황 -> 이 값을 지역 변수가 아닌 필드로 옮기기
		model.setColumnIdentifiers(colNames);
		model.setRowCount(0);
		
		//모델을 테이블에 연결
		table.setModel(model);
		//스크롤이 가능하도록 테이블을 JScrollPane에 감싼다
		JScrollPane scroll=new JScrollPane(table);
		//JScrollPnae을 프레임의 가운데에 배치하기
		add(scroll, BorderLayout.CENTER);
		
		//프레임 객체가 생성되는 시점에 DB에 있는 내용을 읽어와서 출력할 수 있게 해준다.
		//JTable에 출력할 sample row 데이터
		//Object[] row= {1, "글린다", "오즈"};
		//테이블에 연결된 모델에 row 추가하기
		//model.addRow(row);
	
		
		printMember();
		//테이블에 출력할 회원 목록 얻어오기
		//MemberDao dao = new MemberDao();
		//List<MemberDto> list= dao.getList();
		//반복문 돌면서
		//for (MemberDto tmp:list ) {
			//MemberDto 객체에 담긴 회원정보를 이용해서 Object[] 에 담은 다음
			//Object[] rowData = {tmp.getNum(), tmp.getName(), tmp.getAddr()};
			//테이블에 연결된 모델에 추가하기
			//model.addRow(rowData);
		//}
		
		
		//칼럼의 제목 글자 조정
		table.getTableHeader().setFont(new Font("Sans-serif", Font.BOLD, 18));
		table.setFont(new Font("Sans-serif", Font.PLAIN,16));//데이터 글자 크기 14
		table.setRowHeight(25);//각 행의 높이를 조정
		
		
		//테이블에 값이 바뀌었는지 감시할 리스너 등록
		table.addPropertyChangeListener(this);
		
		
	}//생성자 : 프레임 객체가 생성되는 시점에 만들어짐 
	
	public static void main(String[] args) {
		MemberFrame f=new MemberFrame("회원 정보 관리");
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//가독성 좋게+부모자식이라 JFrame.생략하고 
		f.setBounds(100,100,800,500);
		f.setVisible(true);
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		//눌러진 버튼의 action command를 읽어온다
		String command=e.getActionCommand();
		if(command.equals("add")) {//java 에서는 문자열 비교를 .equals()메소드를 이용해서 비교해야 한다.  (자바스크립트에서는 command =="add")
			//입력한 이름과 주소를 읽어 온다
			String name=inputName.getText();//this. 이 생략되어 있는데 JTextField의 참조값이 필드에 저장되어 있어서 생략됨
			String addr=inputAddr.getText();//this. 이 생략되어 있는데 JTextField의 참조값이 필드에 저장되어 있어서 생략됨
			//입력한 이름과 주소를 MEmebrDto 객체에 담는다
			MemberDto dto=new MemberDto();
			dto.setName(name);
			dto.setAddr(addr);
			//MemberDao 객체를 생성해서
			MemberDao dao=new MemberDao();
			//insert() 메소드를 이용해서 회원 정보를 DB에 저장한다
			dao.insert(dto);
			
			
		}else if(command.equals("delete")) {
			int selectedRow=table.getSelectedRow();
			if(selectedRow==-1) {
				JOptionPane.showMessageDialog(this, "삭제할 row를 선택하세요");//자바 frame 창에서 알림을 띄우기 this는 알림을 띄울 컴포넌트(멤버프레임)의 참조값, 알림 메시지를 전달.
				return;// 메소드를 여기서 끝내기
			}
			int num= (int)model.getValueAt(selectedRow, 0);
			new MemberDao().delete(num);
		}
		
		printMember();

		
	}
	
	//회원 목록을 JTable에 출력하는 메소드
	public void printMember() {
		//기존에 출력된 내용을 초기화한 후에
		model.setRowCount(0);
		
		//테이블에 출력할 회원 목록 얻어오기
		MemberDao dao = new MemberDao();
		List<MemberDto> list= dao.getList();
		//반복문 돌면서
		for (MemberDto tmp:list ) {
			//MemberDto 객체에 담긴 회원정보를 이용해서 Object[] 에 담은 다음
			Object[] rowData = {tmp.getNum(), tmp.getName(), tmp.getAddr()};
			//테이블에 연결된 모델에 추가하기 -> 모델을 필드로 만들기
			model.addRow(rowData);
		}
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		/*
		 * property name이 "tableCellEditor"이고 테이블이 수정중이 아닐때
		 * 현재 포커스가 있는 곳의 정보를 모두 읽어와서 DB에 수정반영하기
		 */
		if(evt.getPropertyName().equals("tableCellEditor")&&!table.isEditing()) {
			//현재 포커스가 있는 row의 정보를 DB에 수정 및 반영한다
			//변화된 값을 읽어와서 DB에 반영함
			//수정된 칼럼에 있는 row 전체의 값을 읽어옴
			int selectedIndex=table.getSelectedRow();
			int num=(int)model.getValueAt(selectedIndex, 0);
			String name=(String)model.getValueAt(selectedIndex, 1);
			String addr=(String)model.getValueAt(selectedIndex, 2);
			//읽어온 내용을 MemberDto에 담고
			MemberDto dto=new MemberDto();
			dto.setNum(num);
			dto.setName(name);
			dto.setAddr(addr);
			//MemberDao 객체를 이용해서 수정 반영한다
			new MemberDao().update(dto);
			//선택된 포커스 해제
			table.clearSelection();
		}
		
	}
	
}

 
 
 
자바를 설치하지 않고도 이클립스 실행만으로 자바를 배울 수 있었던 이유 : 이클립스가 자바를 가지고 있다(java runtime environment)

자바가 설치가 되어 있다면 명령 프롬프트에서도 반응이 있다.
 
자바 설치할 필요도 있으니까 설치해보기
https://www.oracle.com/kr/java/technologies/downloads/#java21

Download the Latest Java LTS Free

Subscribe to Java SE and get the most comprehensive Java support available, with 24/7 global access to the experts.

www.oracle.com

 

압축 파일 다운로드 후 압축 해제하기 : 이게 자바 폴더임 → 경로 복사
C:\Users\acorn\Downloads\jdk-21_windows-x64_bin\jdk-21.0.5

 

 

 
 

 
이렇게 해주면 커맨드 창에서 자바 명령어를 사용할 수 있다.(어쩌구저쩌구 있는 내용들이 이제 자바를 사용할 수 있어서 보이는 내용들)

 
 
 
 
vs코드에서 java 만들어보기
파일 이름과 클래스 이름이 동일해야 하는 거 까먹지 않기

지금보니 오타가 여기도 있네 ㅋㅋ 아래에서 오타땜에 에러난거 명령창에도 있는데 ㅋㅋ

이 위에서 만든 거는 소스코드이다. 이것을 컴파일한 MainClass.class 파일이 필요한데, 이클립스는 이걸 자체적으로 다 해줬지만 vs code에서 직접 컴파일하려면 파일에서 우클릭해서 터미널에서 javac로 컴파일해줄 수 있다.(처음에 오류가 났었는데 vscode를 닫았다가 새로 실행하지 않아서 발생한 문제였다)

그래야 명령 프롬프트에서 자바에서  java  MainClass.class를 해줄 수 있다.
 
 
MainClass.java를 컴파일하는 명령어는
javac 컴파일할파일이름.java

 
오후에는 자바로 실행 파일을 직접 만들 것이라고 하심.
4교시에는 내 책 목록에 수정기능 추가해서 에러 안나게 하는거 만들어봐야지 ㅠ
 
 
 
<12:30 4교시>
앞시간에 만든거 참고해서 기능은 완성했고, 나머지 하고싶었던 기능 챗 지피티한테 물어서 칼럼 기둥 폭이랑 가운데 정렬 기능 추가해봄

package test.frame;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;

import test.dao.BtRDao;
import test.dto.BtRDto;

public class BtRFrame extends JFrame implements ActionListener, PropertyChangeListener{
	JTextField inputTitle, inputWriter;
	DefaultTableModel model;
	JTable table;
	
	public BtRFrame(String title) {
		super(title);
		
		setLayout(new BorderLayout());
		JLabel label2=new JLabel("책 제목");
		JLabel label3=new JLabel("저자");
		
		inputTitle = new JTextField(50);
		inputWriter = new JTextField(30);
		
		JButton addBtn=new JButton("추가");
		JButton deleteBtn=new JButton("삭제");
		
		JPanel panel=new JPanel();
		panel.add(label2);
		panel.add(inputTitle);
		panel.add(label3);
		panel.add(inputWriter);
		panel.add(addBtn);
		panel.add(deleteBtn);
		panel.setSize(1500,500);
		
		panel.setBackground(Color.orange);
		add(panel, BorderLayout.NORTH);
		
		addBtn.addActionListener(this);
		deleteBtn.addActionListener(this);
		
		
		addBtn.setActionCommand("add");
		deleteBtn.setActionCommand("delete");
		
		table =new JTable();
		String[] colNames= {"번호", "책 제목", "저자"};
		model=new DefaultTableModel() {
			@Override
			public boolean  isCellEditable(int row, int column) {
				if(column==0) {
					return false;
				}else {
					return true;
				}
			}
		};
		model.setColumnIdentifiers(colNames);
		model.setRowCount(0);
		
		table.setModel(model);
		JScrollPane scroll=new JScrollPane(table);
		add(scroll, BorderLayout.CENTER);

		printBtR();
		
		table.getTableHeader().setFont(new Font("Sans-serif",Font.BOLD, 18));
		table.setFont(new Font("Sans-serif", Font.PLAIN, 16));
		table.setRowHeight(25);
		//table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
		table.getColumnModel().getColumn(0).setMaxWidth(50);
		table.getColumnModel().getColumn(2).setMinWidth(100);
		// 최대 폭을 200으로 설정
		table.getColumnModel().getColumn(0).setCellRenderer(new CenterAlignRenderer());
		table.addPropertyChangeListener(this);
		
	}


	public static void main(String[] args) {
		BtRFrame f=new BtRFrame("읽을 책 목록");
		f.setDefaultCloseOperation(EXIT_ON_CLOSE);
		f.setBounds(20,20, 1500, 1000);
		f.setVisible(true);
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		String command=e.getActionCommand();
		if(command.equals("add")) {
			String title=inputTitle.getText();
			String writer=inputWriter.getText();
			BtRDto dto=new BtRDto();
			dto.setTitle(title);
			dto.setWriter(writer);
			BtRDao dao=new BtRDao();
			dao.insert(dto);
		}else if(command.equals("delete")) {
			int selectedRow=table.getSelectedRow();
			if(selectedRow==-1) {
				JOptionPane.showMessageDialog(this, "삭제할 row를 선택하세요");
				return;
			}
			int num=(int)model.getValueAt(selectedRow, 0);
			new BtRDao().delete(num);
		}
		printBtR();
		
	}
	
	
	public void printBtR() {
		model.setRowCount(0);
		BtRDao dao= new BtRDao();
		List<BtRDto> list=dao.getList();
		for(BtRDto tmp:list) {
			Object[] rowData= {tmp.getNo(), tmp.getTitle(), tmp.getWriter()};
			model.addRow(rowData);
		}
		
	}
    // 텍스트를 가운데 정렬하는 커스텀 셀 렌더러
    public static class CenterAlignRenderer extends DefaultTableCellRenderer {
        public CenterAlignRenderer() {
            setHorizontalAlignment(SwingConstants.CENTER); // 가운데 정렬
        }
    }

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		//System.out.println("property change!");
		//System.out.println("property name:"+evt.getPropertyName());
		
		//System.out.println("isEditing:"+table.isEditing());
		/*
		 *  property name 이 "tableCellEditor" 이고
		 *  table 이 수정중이 아닐때 
		 *  현재 포커스가 있는 곳의 정보를 모두 읽어와서 DB 에 수정반영하기 
		 */
		if(evt.getPropertyName().equals("tableCellEditor") && !table.isEditing()) {
			//현재 포커스가 있는 row 의 정보를 DB 에 수정 반영 한다. 
			//변화된 값을 읽어와서 DB 에 반영한다. 
			//수정된 칼럼에 있는 row  전체의 값을 읽어온다. 
			int selectedIndex=table.getSelectedRow();
			int no=(int)model.getValueAt(selectedIndex, 0);
			String title=(String)model.getValueAt(selectedIndex, 1);
			String writer=(String)model.getValueAt(selectedIndex, 2);
			//수정할 회원의 정보를 MemberDto 객체에 담고 
			BtRDto dto=new BtRDto(no, title, writer);
			//DB에 저장하기 
			new BtRDao().update(dto);
			//선택된 포커스 clear
			table.clearSelection();
		}
	}
	
}