본문 바로가기
내가 공부하려고 올리는/자바

자바 - 자바 초짜 프로젝트 MVC 패턴으로 수정하기

by 결딴력 2021. 10. 23.
반응형

자바를 공부하면서 처음으로 세미 프로젝트(?)를 진행했는데

자바 스윙을 이용해 성적관리 프로그램을 만드는 것이었습니다.

 

프로젝트를 처음으로 진행해보면서 느낀 점은

코딩 자체도 분명 어렵지만 설계가 어렵다는 거였어요😭😭

 

클래스를 어떻게 나눠야 할지

어떤 클래스에 어떤 메소드를 담아야 할지

하나부터 열까지 고민의 연속이었습니다.

 

오늘은 이런 저의 고민을 해결해나가면서

프로젝트를 깔끔하게 수정하려고 합니다.

 

 

1. 현재 프로젝트 상태

 

현재 프로젝트 상태를 먼저 보여드리겠습니다.

우선 아래와 같이 3가지 클래스를 만들어서

프로젝트를 진행했습니다.

FirstView 클래스
SecondView 클래스
Main 클래스

실행 클래스에서는 실행만을 담당하게 하고

FirstView와 SecondView 클래스를 만들었습니다.

 

이렇게 만든 이유는 아래 사진을 보여드리고 설명드리겠습니다.

실행결과(1)
실행결과(2)

실행결과(1) 화면은 FirstView 클래스로 구현한 화면이고

실행결과(2) 화면은 SecondView 클래스로 구현한 화면입니다.

 

실행결과에 동일하게 구현되어야 하는 view가

성적처리 테이블을 제외하고는 동일하기 때문에

SecondView가 FirstView를 상속하게끔 클래스를 설계했고

비슷한 기능을 구현하도록 했습니다.

 

이 클래스를 설계할 때는 상속을 하는 것을 통해

'클래스의 '재사용성'이 높아질 수 있지 않을까?'라는 생각을 했습니다.

 

프로젝트의 실행 결과는 다음과 같습니다.

 

값을 입력하고 연산버튼을 누르면...
총점과 평균, 석차가 자동으로 구해진다!

하지만 막상 프로그램을 짜고

코드 리뷰를 하다 보니 의존성이 너무 높다는 생각이 들었습니다.

 

또한 프로젝트를 진행하면서

내 맘대로 클래스를 구성하기보단

표준화된 규격(?) 같은 설계도가 있어서

그에 맞춰 클래스를 짜면 얼마나 편할까라는 생각도 했습니다.

 

이러한 생각과 의문을 해결하기 위해

MVC 패턴에 대해 알게 됐고,

이 프로젝트에 MVC 패턴을 적용하고자 했습니다.

 

 

2. MVC 패턴이란?

더보기

본 MVC 패턴에 대한 요약글은

유튜브 '우아한 Tech'의 '[10분 테코톡] 🧀 제리의 MVC 패턴'을 참고하여 작성했습니다.

MVC 패턴을 간략하게 설명해보겠습니다.

MVC 패턴은 'Model-View-Controller' 패턴의 약자

프로그램의 유지보수가 편해지는 코드 구성을 위한

디자인 패턴을 의미합니다.

 

View 클래스에서는 

Model 클래스를 표현하기 위해

사용자 인터페이스(UI)를 제공합니다.

 

Model 클래스

프로그램의 Logic과 데이터를 담당합니다.

 

Controller 클래스

View와 Model 사이에 위치하여

사용자로부터 정보를 입력받으면

Model을 통해 정보를 처리합니다.

이후 처리한 정보를 View에게 전달합니다.

 

MVC 패턴을 프로그램에 적용하기 위해서

다음과 같은 규칙을 따릅니다.

  1. Model은 Controller와 View에 의존하지 않아야 한다.
    (Model 내부에 Controller와 View에 관련된 코드가 있으면 안 된다.)
  2. View는 Model에만 의존해야 하고, Controller에 의존하면 안 된다.
    (View 내부에 Model의 코드만 있을 수 있고, Controller의 코드가 있으면 안 된다.)
  3. View가 Model로부터 데이터를 받을 때는, 사용자마다 다르게
    보여주어야 하는 데이터에 대해서만 받아야 한다.
  4. Controller는 Model과 View에 의존해도 된다.
    (Controller 내부에는 Model과 View의 코드가 있을 수 있다.)
  5. View가 Model로부터 데이터를 받을 때, 반드시 Controller에서 받아야 한다.

자 그럼 위 5가지 규칙을 지키며

저의 프로젝트를 수정해보겠습니다.

 

3. 프로젝트 수정하기

우선 직관적으로 클래스를 나누기 위해

클래스의 이름을 변경하겠습니다.

 

MVC 패턴을 적용하는 연습이므로

MVC의 약자를 따서

MManageGrade, VManageGrade, CManageGrade

클래스의 이름을 변경했습니다.

 

MManageGrade는 model,

VManageGrade는 view,

CManageGrade는 controller를 의미합니다.

 

우선 VManageGrade 클래스를 설계하겠습니다.

view 클래스로 설계하고자 하는 최종 결과물은

다음 사진과 같습니다.

View 클래스로 만들 최종 목표물

위와 같은 화면을 만들기 위해서

다음과 같이 코드를 작성했습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;
 
public class VManageGrade {
 
 
    public DefaultTableModel getModel() { 
        return model;
    }
 
    public void setModel(DefaultTableModel model) {
        this.model = model;
    }
 
    private JFrame           jframe = null;
    private JPanel          jPanel1 = null;
    private JPanel          jPanel2 = null;
    private JPanel          jPanel3 = null;
    private JPanel          jPanel4 = null;
    private JLabel           label1 = null;
    private JLabel           label2 = null;
    private JTextField          jtf = null;
    private JButton            jbt1    = null;
    private JButton            jbt2 = null;
    private JButton            jbt3 = null;
    private DefaultTableModel model = null;
    private JTable            table = null;
    private JScrollPane      scroll = null;
    private int                 number;
    private String         header[];
    private String     contents[][];
    
 
    private void initComp() { //프레임 초기화
         jframe          = new JFrame("성적처리");           
         jPanel1         = new JPanel();                 
         jPanel2         = new JPanel();                 
         jPanel3         = new JPanel();
         jPanel4         = new JPanel(); 
         label1          = new JLabel("성적처리인원수");        
         label2          = new JLabel("명");              
         jtf             = new JTextField(10);           
         jbt1            = new JButton("만들기");           
         jbt2            = new JButton("연산");
         jbt3            = new JButton("종료");
         header             = new String[] { "이름""국어""영어""수학""총점""평균""석차"};
          
    }
    
    private void setFrame() { //프레임 세팅
        jframe.setSize(600400);
        jframe.setLocationRelativeTo(null);
        jframe.setVisible(true);
        jframe.setDefaultCloseOperation(jframe.EXIT_ON_CLOSE);
    }
    
    private void setLayout() { //프레임 레이아웃 설정
        jPanel1.setLayout(new BorderLayout());
 
        jPanel2.add(label2);
        jPanel2.add(jbt1);
 
        jPanel1.add("West", label1);
        jPanel1.add("Center", jtf);
        jPanel1.add("East", jPanel2);
        jframe.add("North", jPanel1);
 
        jPanel4.setLayout(new BorderLayout());
        jPanel3.add(jbt2);
        jPanel3.add(jbt3);
        jPanel4.add("East", jPanel3);
        jframe.add("South", jPanel4);
        
    }
    
    public int setNumber() { //테이블 만들기 위한 입력값
        try {
            Integer.parseInt(jtf.getText());
            number = Integer.parseInt(jtf.getText());
        } catch (NumberFormatException e) {
            System.out.println(e.toString());
            System.out.println("값을 입력하세요.");
            number = 0;
        } finally {
            return number;
        }
    }
    
    public int getNumber() {
        return number;
    }
    
    public void setTable() { //테이블 만들기
        int num         = getNumber();
        contents        = new String[num][];
        model           = new DefaultTableModel(contents, header);
        table           = new JTable(model);
        scroll          = new JScrollPane(table);
        jframe.add("Center", scroll);
    }
    
    public void Action1(ActionListener listener) { //jbt1 눌렀을 때 이벤트
        jbt1.addActionListener(listener);
    } 
    
    public void Action2(ActionListener listener) { //jbt2 눌렀을 때 이벤트
        jbt2.addActionListener(listener);
    }
    
    public void Action3(ActionListener listener) { //jbt3 눌렀을 때 이벤트
        jbt3.addActionListener(listener);
    }
    
    public void refresh() { //프레임 새로고침
        jframe.revalidate();
        jframe.repaint();
    }
    
    public VManageGrade() { //view 클래스 생성자
        initComp();
        setFrame();
        setLayout();
    } 
   
                                  
}                          
cs

 

이번에는 Model 클래스를 설계하겠습니다.

Model 클래스에서는

합계를 구하는 메소드

평균을 구하는 메소드

석차를 구하기 위한 배열 선언을 했습니다.

코드는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 public class MManageGrade {
    
    private int total;
    private float avg;
    private int[][] array;
 
    public int[][] getArray() {
        return array;
    }
 
    public void setArray(int[][] array) {
        this.array = array;
    }
 
    public float getAvg() {
        return avg;
    }
 
    public void setAvg(float avg) {
        this.avg = avg;
    }
 
    public int getTotal() {
        return total;
    }
 
    public void setTotal(int total) {
        this.total = total;
    }
    
    
    public void CalTotal(int num1, int num2, int num3) { //합계 구하기 메소드
            total = num1+num2+num3;
            setTotal(total);
    }
    
    
    public void CalAvg(int total) { //평균 구하기 메소드
        avg = getTotal()/3.0f;
        setAvg(avg);
    }
    
    
}   
cs

 

마지막으로 controller 클래스를 설계하겠습니다.

각 버튼을 눌렀을 때

controller 클래스에서 이벤트 처리를 할 수 있도록

클래스를 설계했습니다.

코드는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package managegrade;
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
 
public class CManageGrade {
    private MManageGrade model;
    private VManageGrade view;
    
    public CManageGrade(MManageGrade model, VManageGrade view) { //controller 클래스 생성자
        this.model = model;
        this.view  = view;
        ActionListener1();
        ActionListener2();
        ActionListener3();
    }
    
    public void ActionListener1() { //jbt1 눌렀을 때 이벤트 정의
        this.view.Action1(new ActionListener() {
        
            @Override
            public void actionPerformed(ActionEvent e) {
                view.setNumber(); //테이블 값설정
                view.setTable(); //값에 따른 테이블 생성
                view.refresh(); //프레임 새로고침
            }
        });    
    }
    
    public void ActionListener2() { //jbt2 눌렀을 때 이벤트 정의
        this.view.Action2(new ActionListener() {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                model.setArray(new int[view.getNumber()][2]); //array 크기 설정
                for(int i = 0; i<view.getNumber(); i++) {
                    int num1 = Integer.parseInt((String)view.getModel().getValueAt(i, 1)); //view클래스의 테이블 i행 1열 값 가져오기
                    int num2 = Integer.parseInt((String)view.getModel().getValueAt(i, 2)); //view클래스의 테이블 i행 2열 값 가져오기
                    int num3 = Integer.parseInt((String)view.getModel().getValueAt(i, 3)); //view클래스의 테이블 i행 3열 값 가져오기
                    model.CalTotal(num1, num2, num3); //i행 1,2,3열 더하기 -> model클래스 메소드 이용
                    model.CalAvg(model.getTotal()); //더한 값 평균값 구하기 -> model클래스 메소드 이용
                    
                    view.getModel().setValueAt(String.valueOf(model.getTotal()), i, 4); //view클래스 테이블의 i행 4열 값을 설정
                    view.getModel().setValueAt(String.valueOf(model.getAvg()), i, 5); //view클래스 테이블의 i행 5열 값을 설정
                    
                    model.getArray()[i][0= model.getTotal(); //array 값 설정
                    model.getArray()[i][1= 1;    //array 값 설정
                }
                
                for(int i=0; i<view.getNumber(); i++) { //석차 구하기
                    for(int j=0; j<view.getNumber(); j++) {
                        if(model.getArray()[i][0< model.getArray()[j][0]) {
                            model.getArray()[i][1]++;
                        }
                    }
                }
                
                for(int i=0; i<view.getNumber(); i++) { //구한 석차 입력
                    view.getModel().setValueAt(String.valueOf(model.getArray()[i][1]), i, 6);
                }                
            }
        });    
    }
    
    public void ActionListener3() {
        this.view.Action3(new ActionListener() {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
                
            }
        });    
    }
    
}
cs

MVC 패턴을 적용한

성적관리 프로그램을 돌리기 위한

Test 클래스를 다음과 같이 선언하고

실행을 시켜보겠습니다.

1
2
3
4
5
6
7
8
9
10
public class Test {
 
    
    public static void main(String[] args) {
        MManageGrade model = new MManageGrade();
        VManageGrade view = new VManageGrade();
        CManageGrade controller = new CManageGrade(model, view);
    }
 
}
cs

실행결과는 다음과 같습니다.

 

테스트 결과

정상적으로 작동하는 것을

테스트를 통해서 확인할 수 있었습니다.

 

 

4. 마치며

오늘은 진행했던 프로젝트를

MVC 디자인 패턴 적용을 위해

수정해보는 시간을 가졌습니다.

 

앞서 설명드렸던

MVC 패턴을 적용하기 위한

5가지 규칙을 지키면서

설계하려고 노력했고

 

각 클래스의 의존성은 최대한 낮추고

재사용성을 높이기 위해 집중했습니다.

 

아직도 부족한 점이 많다고 느껴지지만

MVC 패턴을 공부하고

최초로 적용해보려고 노력한 것에

스스로 조금은 뿌듯함을 느끼고 있습니다.😂

 

다음 프로젝트를 진행할 때는

조금 더 MVC 패턴을 능숙하게 적용하고 싶네요.

 

이 글에 대한 피드백 사항이 있다면

댓글이나 메일 꼭 부탁드립니다!!🙇‍♂️🙇‍♂️

반응형

댓글