본문 바로가기

JAVA

[Java] DB 내 정보 변경, 탈퇴 /관리자 모드 아이디 삭제

0. 소개

이전 글에서는 로그인과 회원정보를 이용해, 회원정보를 만들어 로그인을 사용했었다.

주로 사용한 SQL문은 SELECT와 INSERT문이였으며, 이번에는 DELETE, UPDATE로 데이터 값 변경을 시도한다.

이 전 글을 보고싶다면 아래에 링크를 남기겠다.

2022.05.02 - [JAVA] - [Java] DB 회원가입, 로그인 시스템 만들기

 

1. 기능

  • 로그인 완료시 내 정보 화면으로 이동하며, 내 정보에서 데이터 값을 변경할 수 있다.
  • 관리자모드에서는 모든 회원 아이디들을 불러오며, 관리자를 제외한 모든 아이디를 삭제할 수 있다.

 

2. 주요 메소드

-로그인 관리-

로그인 완료 시에 그 시점의 id값을 가져와야 하므로 setid(String id)를 이용하여, 값을 넘겨준다.

그리고 내 정보 화면에서는 id값을 다시 불러와서 로그인 한 사용자의 정보를 바꿀 수 있게 확신해준다.

 

-내 정보 변경-

String SQL = "update user_info set phoneNumber=? where id=?"; 

String SQL2 = "delete from user_info where id=?";

위와 같은 SQL문으로 내 정보를 변경할 수 있게 하였다. 여기서 id는 위에서 setid( )의 id다.

 

-회원 아이디 다 불러오기-

관리자 모드의 화면에서 콤보박스를 클릭했을 때 그 안에 모든 회원 아이디가 보이게 한다.

String SQL = "SELECT ID FROM user_info"; 다음과 같은 SQL문으로 ID를 전부 불러온다.

그리고 실행된 쿼리문에서 있는 ID 숫자만큼 List에 넣는다. 

JComboBox<String> idComboBox = new JComboBox<String>(idList.toArray(new String[idList.size()]));

그리고 다음과 같이 JComboBox에서 List을 배열로 변환해주고 JComboBox에 넣어준다. 이렇게 하면 JComboBox에    모든 회원의 아이디가 나타나게 된다. 

 

-임의로 회원 아이디 삭제하기-

위와 같이 JComboBox를 이용해 회원정보를 가져왔다면 편리하게 처리할 수 있다. 탈퇴시키고 싶은 회원은 JComboBox의 이벤트 처리로 값을 가져오고, 강제 회원탈퇴를 클릭했을 때 DELETE SQL문을 사용하면 쉽게 탈퇴시킬 수 있다.

 

3. 코드

import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JRadioButton;
import javax.swing.JTextField;

public class LoginProject {

	JPanel cardPanel;
	LoginProject lp;
	CardLayout card;
	String id = null;

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		LoginProject lp = new LoginProject();
		lp.setFrame(lp);
	}

	public void setFrame(LoginProject lpro) {

		JFrame jf = new JFrame();
		LoginPanel lp = new LoginPanel(lpro);
		signupPanel sp = new signupPanel(lpro);
		userMyPage ump = new userMyPage(lpro);
		adminUser au = new adminUser(lpro);

		card = new CardLayout();

		cardPanel = new JPanel(card);
		cardPanel.add(lp.mainPanel, "Login");
		cardPanel.add(sp.mainPanel, "Register");
		cardPanel.add(ump.mainPanel, "My Page");
		cardPanel.add(au.mainPanel, "Admin Page");
		
		jf.add(cardPanel);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setSize(500, 700);
		jf.setVisible(true);
	}

	public Connection getConnection() throws SQLException {
		Connection conn = null;

		conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/login_DB?serverTimezone=UTC", "root",
				"1234");

		return conn;
	}
	
	public void setid(String id) {
		this.id = id;
	}

}

class LoginPanel extends JPanel implements ActionListener {

	JPanel mainPanel;
	JTextField idTextField;
	JPasswordField passTextField;

	String userMode = "일반";
	LoginProject lp;
	Font font = new Font("회원가입", Font.BOLD, 40);
	String admin = "admin";

	public LoginPanel(LoginProject lp) {
		this.lp = lp;
		
		mainPanel = new JPanel();
		mainPanel.setLayout(new GridLayout(5, 1));

		JPanel centerPanel = new JPanel();
		JLabel loginLabel = new JLabel("로그인 화면");
		loginLabel.setFont(font);
		centerPanel.add(loginLabel);

		JPanel userPanel = new JPanel();

		JPanel gridBagidInfo = new JPanel(new GridBagLayout());
		gridBagidInfo.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25));
		GridBagConstraints c = new GridBagConstraints();

		JLabel idLabel = new JLabel(" 아이디 : ");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 0;
		gridBagidInfo.add(idLabel, c);

		idTextField = new JTextField(15);
		c.insets = new Insets(0, 5, 0, 0);
		c.gridx = 1;
		c.gridy = 0;
		gridBagidInfo.add(idTextField, c);

		JLabel passLabel = new JLabel(" 비밀번호 : ");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 1;
		c.insets = new Insets(20, 0, 0, 0);
		gridBagidInfo.add(passLabel, c);

		passTextField = new JPasswordField(15);
		c.insets = new Insets(20, 5, 0, 0);
		c.gridx = 1;
		c.gridy = 1;
		gridBagidInfo.add(passTextField, c);

		JPanel loginPanel = new JPanel();
		JButton loginButton = new JButton("로그인");
		loginPanel.add(loginButton);

		JPanel signupPanel = new JPanel();
		JButton signupButton = new JButton("회원가입");
		loginPanel.add(signupButton);

		mainPanel.add(centerPanel);
		mainPanel.add(userPanel);
		mainPanel.add(gridBagidInfo);
		mainPanel.add(loginPanel);
		mainPanel.add(signupPanel);


		loginButton.addActionListener(this);

		signupButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				lp.card.next(lp.cardPanel);
			}
		});

	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		JButton jb = (JButton) e.getSource();

		switch (e.getActionCommand()) {

		case "일반":
			userMode = "일반";
			break;

		case "관리자":
			userMode = "관리자";
			break;

		case "로그인":

			String id = idTextField.getText();
			String pass = passTextField.getText();

			try {

				String sql_query = String.format("SELECT password FROM user_info WHERE id = '%s' AND password ='%s'",
						id, pass);

				Connection conn = lp.getConnection();
				Statement stmt = conn.createStatement();

				ResultSet rset = stmt.executeQuery(sql_query);
				rset.next();
				
				if (pass.equals(rset.getString(1)) && idTextField.getText().equals(admin)) {
					JOptionPane.showMessageDialog(this, "Login Success", "로그인 성공", 1);
					lp.card.show(lp.cardPanel, "Admin Page");
				}
				else if (pass.equals(rset.getString(1))) {
					JOptionPane.showMessageDialog(this, "Login Success", "로그인 성공", 1);
					lp.setid(idTextField.getText()); // 로그인하고 로그인 아이디 정보 가질 수 있게.
					lp.card.show(lp.cardPanel, "My Page");

				} else
					JOptionPane.showMessageDialog(this, "Login Failed", "로그인 실패", 1);

			} catch (SQLException ex) {
				JOptionPane.showMessageDialog(this, "Login Failed", "로그인 실패", 1);
				System.out.println("SQLException" + ex);
			}

			break;

		}
	}

} // class LoginPanel

class signupPanel extends JPanel {

	JTextField idTf;
	JPasswordField passTf;
	JPasswordField passReTf;
	JTextField nameTf;
	JTextField yearTf;
	JTextField phoneTf;
	JPanel mainPanel;
	JPanel subPanel;
	JComboBox<String> monthComboBox;
	JComboBox<String> dayComboBox;
	JRadioButton menButton;
	JRadioButton girlButton;
	JButton registerButton;
	Font font = new Font("회원가입", Font.BOLD, 40);

	String year = "", month = "", day = "";
	String id = "", pass = "", passRe = "", name = "", sex = "", phone = "";
	LoginProject lp;

	public signupPanel(LoginProject lp) {

		this.lp = lp;
		subPanel = new JPanel();
		subPanel.setLayout(new GridBagLayout());
		subPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25));

		JLabel idLabel = new JLabel("아이디 : ");
		JLabel passLabel = new JLabel("비밀번호 : ");
		JLabel passReLabel = new JLabel("비밀번호 재확인 : ");
		JLabel nameLabel = new JLabel("이름 : ");
		JLabel birthLabel = new JLabel("생년월일 : ");
		JLabel sexLabel = new JLabel("성별 : ");
		JLabel phoneLabel = new JLabel("핸드폰번호 : ");

		idTf = new JTextField(15);
		passTf = new JPasswordField(15);
		passReTf = new JPasswordField(15);
		nameTf = new JTextField(15);
		yearTf = new JTextField(4);
		phoneTf = new JTextField(11);

		monthComboBox = new JComboBox<String>(
				new String[] { "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12" });
		dayComboBox = new JComboBox<String>(new String[] { "01", "02", "03", "04", "05", "06", "07", "08", "09", "10",
				"11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27",
				"28", "29", "30", "31" });

		menButton = new JRadioButton("남자");
		girlButton = new JRadioButton("여자");
		ButtonGroup sexGroup = new ButtonGroup();
		sexGroup.add(menButton);
		sexGroup.add(girlButton);

		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.HORIZONTAL;
		c.insets = new Insets(15, 5, 0, 0);

		c.gridx = 0;
		c.gridy = 0;
		subPanel.add(idLabel, c);

		c.gridx = 1;
		c.gridy = 0;
		subPanel.add(idTf, c); // 아이디

		c.gridx = 0;
		c.gridy = 1;
		subPanel.add(passLabel, c);

		c.gridx = 1;
		c.gridy = 1;
		subPanel.add(passTf, c); // pass
		
		c.gridx = 2;
		c.gridy = 1; 
		subPanel.add(new JLabel("특수문자 + 8자"),c); //보안설정

		c.gridx = 0;
		c.gridy = 2;
		subPanel.add(passReLabel, c);

		c.gridx = 1;
		c.gridy = 2;
		subPanel.add(passReTf, c); // password 재확인

		c.gridx = 0;
		c.gridy = 3;
		subPanel.add(nameLabel, c);

		c.gridx = 1;
		c.gridy = 3;
		subPanel.add(nameTf, c); // 이름

		c.gridx = 0;
		c.gridy = 4;
		subPanel.add(birthLabel, c);

		c.gridx = 1;
		c.gridy = 4;
		c.weightx = 0.6;
		subPanel.add(yearTf, c);

		c.gridx = 2;
		c.gridy = 4;
		c.weightx = 0.2;
		subPanel.add(monthComboBox, c);

		c.gridx = 3;
		c.gridy = 4;
		c.weightx = 0.2;
		subPanel.add(dayComboBox, c);

		c.gridx = 0;
		c.gridy = 5;
		subPanel.add(sexLabel, c);

		c.gridx = 1;
		c.gridy = 5;
		subPanel.add(menButton, c);

		c.gridx = 2;
		c.gridy = 5;
		subPanel.add(girlButton, c);

		c.gridx = 0;
		c.gridy = 6;
		subPanel.add(phoneLabel, c);

		c.gridx = 1;
		c.gridy = 6;
		subPanel.add(phoneTf, c);

		mainPanel = new JPanel();
		mainPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25));
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
		JLabel signupLabel = new JLabel("회원가입 화면 ");
		signupLabel.setFont(font);
		signupLabel.setAlignmentX(Component.CENTER_ALIGNMENT);

		registerButton = new JButton("회원가입");
		registerButton.setAlignmentX(Component.CENTER_ALIGNMENT);

		mainPanel.add(signupLabel);
		mainPanel.add(subPanel);
		mainPanel.add(registerButton);

		monthComboBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (e.getSource() == monthComboBox) {
					JComboBox monthBox = (JComboBox) e.getSource();
					month = (String) monthBox.getSelectedItem();
					System.out.println(month);
				}

			}
		});
		dayComboBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				if (e.getSource() == dayComboBox) {
					JComboBox dayBox = (JComboBox) e.getSource();
					day = (String) dayBox.getSelectedItem();
					System.out.println(month);
				}
			}
		});

		menButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				sex = e.getActionCommand();
			}
		});

		girlButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				sex = e.getActionCommand();
			}
		});
		registerButton.addActionListener(new ActionListener() {      //회원가입버튼

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				id = idTf.getText();
				pass = new String(passTf.getPassword());
				passRe = new String(passReTf.getPassword());
				name = nameTf.getText();
				year = yearTf.getText();
				phone = phoneTf.getText();

				String sql = "insert into user_info(id, password, name, birthday, sex, phoneNumber) values (?,?,?,?,?,?)";

				Pattern passPattern1 = Pattern.compile("^(?=.*[a-zA-Z])(?=.*\\d)(?=.*\\W).{8,20}$"); //8자 영문+특문+숫자
				Matcher passMatcher = passPattern1.matcher(pass);

				if (!passMatcher.find()) {
					JOptionPane.showMessageDialog(null, "비밀번호는 영문+특수문자+숫자 8자로 구성되어야 합니다", "비밀번호 오류", 1);
				} else if (!pass.equals(passRe)) {
					JOptionPane.showMessageDialog(null, "비밀번호가 서로 맞지 않습니다", "비밀번호 오류", 1);

				} else {
					try {
						Connection conn = lp.getConnection();

						PreparedStatement pstmt = conn.prepareStatement(sql);

						String date = yearTf.getText() + "-" + month + "-" + day;

						pstmt.setString(1, idTf.getText());
						pstmt.setString(2, pass);
						pstmt.setString(3, nameTf.getText());
						pstmt.setString(4, date);
						pstmt.setString(5, sex);
						pstmt.setString(6, phoneTf.getText());

						int r = pstmt.executeUpdate();
						System.out.println("변경된 row " + r);
						JOptionPane.showMessageDialog(null, "회원 가입 완료!", "회원가입", 1);
						lp.card.previous(lp.cardPanel); // 다 완료되면 로그인 화면으로
					} catch (SQLException e1) {
						System.out.println("SQL error" + e1.getMessage());
						if (e1.getMessage().contains("PRIMARY")) {
							JOptionPane.showMessageDialog(null, "아이디 중복!", "아이디 중복 오류", 1);
						} else
							JOptionPane.showMessageDialog(null, "정보를 제대로 입력해주세요!", "오류", 1);
					} // try ,catch
				}
			}
		});

	}
}



class userMyPage extends JPanel implements ActionListener {

	JPanel mainPanel;
	JPanel centerPanel;
	JPanel downPanel;
	JButton logoutButton;
	JButton unregisterButton;
	LoginProject lp;
	Font font = new Font("회원가입", Font.BOLD, 40);

	JTextField changepassTF;
	JTextField changephoneTF;

	JButton changepassButton;
	JButton changephoneButton;

	public userMyPage(LoginProject lp) {

		this.lp = lp;

		mainPanel = new JPanel();
		mainPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25));
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));

		// top
		JLabel mypageLabel = new JLabel("내 정보");
		mypageLabel.setFont(font);
		mypageLabel.setAlignmentX(Component.CENTER_ALIGNMENT);

		// center

		centerPanel = new JPanel(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		changepassTF = new JTextField(13);
		changephoneTF = new JTextField(12);
		changepassButton = new JButton("비밀번호 바꾸기");
		changephoneButton = new JButton("전화번호 바꾸기");

		c.fill = GridBagConstraints.HORIZONTAL;
		c.insets = new Insets(5, 5, 0, 0);

		c.gridx = 0;
		c.gridy = 0;
		centerPanel.add(changephoneTF, c);

		c.gridx = 1;
		c.gridy = 0;
		centerPanel.add(changephoneButton, c);

		c.gridx = 0;
		c.gridy = 1;
		centerPanel.add(changepassTF, c);

		c.gridx = 1;
		c.gridy = 1;
		centerPanel.add(changepassButton, c);

		// down
		downPanel = new JPanel();
		logoutButton = new JButton("로그아웃");
		unregisterButton = new JButton("탈퇴");
		downPanel.add(logoutButton);
		downPanel.add(unregisterButton);

		mainPanel.add(mypageLabel);
		mainPanel.add(centerPanel);
		mainPanel.add(downPanel);

		logoutButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				lp.card.first(lp.cardPanel);
			}
		});

		changephoneButton.addActionListener(this);
		changepassButton.addActionListener(this);
		unregisterButton.addActionListener(this);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub

		Connection conn = null;
		PreparedStatement pstmt = null;
		// System.out.println(lp.id);

		try {
			conn = lp.getConnection();

			switch (e.getActionCommand()) {

			case "전화번호 바꾸기":
				String SQL = "update user_info set phoneNumber=? where id=?";
				pstmt = conn.prepareStatement(SQL);

				pstmt.setString(1, changephoneTF.getText());
				pstmt.setString(2, lp.id);

				int r = pstmt.executeUpdate();
				System.out.println("변경된 row : " + r);

				break;

			case "비밀번호 바꾸기":
				String SQL1 = "update user_info set password=? where id=?";
				pstmt = conn.prepareStatement(SQL1);

				pstmt.setString(1, changepassTF.getText());
				pstmt.setString(2, lp.id);

				int r1 = pstmt.executeUpdate();
				System.out.println("변경된 row : " + r1);
				break;

			case "탈퇴":

				String SQL2 = "delete from user_info where id=?";
				pstmt = conn.prepareStatement(SQL2);

				int result = JOptionPane.showConfirmDialog(null, "정말로 탈퇴하시겠습니까?", "Confirm message",
						JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
				if (result == JOptionPane.YES_OPTION) {
					pstmt.setString(1, lp.id);
					int r2 = pstmt.executeUpdate();
					System.out.println("변경된 row: " + r2);
					lp.card.first(lp.cardPanel);
				} else
					break;

				break;

			default:
				break;
			}

		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

	}

}// userinfo 클래스

class adminUser implements ActionListener {

	JPanel mainPanel;
	Font font = new Font("회원가입", Font.BOLD, 40);
	LoginProject lp;
	String unregisterID;

	public adminUser(LoginProject lp) {

		this.lp = lp;

		List<String> idList = new ArrayList<String>(getIDs());

		JLabel adminpageLabel = new JLabel("관리자 모드");
		adminpageLabel.setFont(font);
		adminpageLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
		JComboBox<String> idComboBox = new JComboBox<String>(idList.toArray(new String[idList.size()]));
		JButton logoutButton = new JButton("로그아웃");
		JButton unregisterButton = new JButton("강제탈퇴");

		JPanel centerPanel = new JPanel();
		centerPanel.add(idComboBox);
		centerPanel.add(unregisterButton);

		mainPanel = new JPanel();
		mainPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25));
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
		mainPanel.add(adminpageLabel);
		mainPanel.add(centerPanel);
		mainPanel.add(logoutButton);

		idComboBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				JComboBox<String> idBox = (JComboBox) e.getSource();
				unregisterID = idBox.getSelectedItem().toString();
			}
		}); // idComboBox 이벤트 처리

		logoutButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				lp.card.first(lp.cardPanel);
			}
		}); // 로그아웃 이벤트 처리

		unregisterButton.addActionListener(this);

	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		try {
			Connection conn = lp.getConnection();
			String SQL2 = "delete from user_info where id=?";
			PreparedStatement pstmt = conn.prepareStatement(SQL2);

			if (unregisterID.equals("admin")) {
				JOptionPane.showMessageDialog(null, "관리자 계정은 지울수 없습니다.", "삭제 실패", 1);
			} else {
				int result = JOptionPane.showConfirmDialog(null, "정말로 탈퇴하시겠습니까?", "Confirm message",
						JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
				if (result == JOptionPane.YES_OPTION) {
					pstmt.setString(1, unregisterID);
					int r2 = pstmt.executeUpdate();
					System.out.println("변경된 row: " + r2);
				}

			}
		

		} catch (SQLException e5) {
			e5.printStackTrace();
		}
	}//회원탈퇴
	
	public List<String> getIDs() {
		List<String> list = new ArrayList<String>();
		
		try {
			Connection conn = lp.getConnection();
			String SQL = "SELECT ID FROM user_info";
			Statement stmt = conn.createStatement();

			ResultSet rs = stmt.executeQuery(SQL);

			while (rs.next()) {
				list.add(rs.getString("id"));
			}

		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return list;
	}
}

 

4. 실행화면

 

아래 사진에서는 관리자모드에서 JComboBox가 모든 사용자의 아이디가 가져오는걸 볼 수 있다.

그림1. 관리자모드

 

그림2. 내 사용자 정보

 

5. 부족한점

  • 먼저 블로그에 사진을 올리다 보니 실행 JFrame에 외곽선이 없어서, 경계선이 구분이  잘 되지 않는다.
  • UI가 부족하다. 직접 일일이 추가하다 보니 시간이 오래 걸리고, 생각처럼 위치에 가지 않는다.
  • 내 정보 탭에서는 Layout을 탭으로 이동할 수 있게 만들어, 처리했으면 더 깔끔했을 것 같다.
  • 지금은 4개의 패널이 있지만, 더 많은 패널이 있을 때에는 일일이 각 패널을 만들기는 힘들 것이다. 약간 인터페이스 같은 느낌으로 어느 정도 추상화된 패널로 상속받아 사용하면 좀 더 재사용성을 높일 수 있지 않을까라는 생각을 해본다.