관리 메뉴

거니의 velog

(1) 프로그램의 발전 과정 본문

Java_Servlet

(1) 프로그램의 발전 과정

Unlimited00 2023. 8. 21. 08:53

* 웹 브라우저를 통해 인터넷에 접속한다. 그리고 접속한 홈페이지에서 텍스트나 이미지 같은 요소를 클릭해 다른 웹 페이지로 이동한다.

* 일반적인 웹 페이지는 대부분 미리 서버에 등록해 두었다가 웹 브라우저가 서버에 특정 데이터를 요청하면 이를 웹 브라우저로 전송해서 보여준다. 이를 가능하게 하는 기술 중 하나가 JSP(Java Server Page, 자바 서버 페이지) 이다.


1. 클라이언트 PC 기반 프로그램

* 자바로 일반 클라이언트 PC에서 클라이언트가 직접 설치해서 사용하는 환율 계산기를 구현했다. 이를 자바로 구현하면 대략 다음과 같다.

package ex01;

import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;


public class Calculator extends JFrame {
	private static float USD_RATE= 1124.70F;
	private static float JPY_RATE = 10.113F;
	private static float CNY_RATE = 163.30F;
	private static float GBP_RATE = 1444.35F;
	private static float EUR_RATE = 1295.97F;
			
	JLabel title = new JLabel("달러");
	JTextField operand1 = new JTextField(10);
	String[] opExpression = {"선택","달러", "엔화", "위안","파운드","유로"};
	JComboBox<String> opSelection = new JComboBox<String>(opExpression);
	JTextField txtResult = new JTextField(10);
	JButton btnClear = new JButton("다시입력");
	
	public Calculator() {
		Container contentPane = this.getContentPane();
		contentPane.setLayout(new FlowLayout());
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.pack();
		this.setVisible(true);
	}
	
	private void startFrame(){
		opSelection.addActionListener(new SelectionHandler());
		btnClear.addActionListener(new SelectionHandler());
		this.setTitle("환율 계산하기");
		this.add(title);
		this.add(operand1);
		this.add(opSelection);
		this.add(txtResult);
		this.add(btnClear);
		this.setSize(700,200);
		
	}
	
	class SelectionHandler implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			if(e.getSource()==opSelection){
				calculate();
			}else if(e.getSource()==btnClear){
				init();
			}
			
		}
	}

	private void calculate(){
		float won=Float.parseFloat(operand1.getText());
		String result=null;
		String operator=opSelection.getSelectedItem().toString();
		if(operator.equals("달러")){
			result=String.format("%.6f",won/USD_RATE);
		}else if(operator.equals("엔화")){
			result=String.format("%.6f",won/JPY_RATE);
		}else if(operator.equals("위안")){
			result=String.format("%.6f",won/CNY_RATE);
		}else if(operator.equals("파운드")) {
			result=String.format("%.6f",won/GBP_RATE);
		}else if(operator.equals("유로")) {
			result=String.format("%.6f",won/EUR_RATE);
		}
		txtResult.setText(result);
	}
	
	private void init(){
		operand1.setText("");
		txtResult.setText("");
	}
	public static void main(String[] args){
		Calculator calc=new Calculator();
		calc.startFrame();
		
	}

}

* 여기에 파운드와 유로로 변환해 주는 기능을 추가하면? 

- 이 클라이언트 기반 프로그램의 문제점은 무엇일까? 처음에는 달러, 엔화, 위안으로만 환율을 변환했으나, 이제는 파운드와 유로로의 변환 기능이 추가되었다. 외화의 종류는 이 외에도 수없이 많으므로 이 프로그램을 폭넓게 확장하려면 앞으로도 기능 추가는 피할 수 없다.

- 이처럼 기능이 자주 변경되는 프로그램이라면 수시로 사용자 PC마다 프로그램을 업데이트하거나 새로 설치해야 한다는 문제점이 있다. 인터넷 이전 PC 기반 프로그램은 기능이나 화면 형태가 바뀌면 코드를 일일히 추가한 후 또 일일이 PC에 다시 설치하거나 업데이트 해야 했다.

- 이는 사용자나 개발자 모두에게 상당한 불편함을 초래할 뿐 아니라, 클라이언트 프로그램에 데이터베이스 접속 정보라도 있으면 정보 보안에 취약하다는 문제점이 있다. 그래서 이를 보완하여 나온 것이 클라이언트-서버 기반 프로그램이다.


2. 클라이언트-서버 기반 프로그램

* 이 구조는 기존 클라이언트가 수행하는 모든 기능을 서버에서 수행한다. 클라이언트의 기능은 대폭 축소되어, 클라이언트는 처리할 데이터가 있으면 네트워크를 통해 서버에 전달하고, 서버가 처리한 결과를 네트워크를 통해 다시 받아 결과를 화면에 출력하는 역할만 한다.

* 다음 코드는 환율 계산기 프로그램에서 서버가 담당하는 기능을 구현한 소스 코드이다. 클라이언트가 전송한 데이터와 연산자를 이용해서 계산한 후 결과를 클라이언트에 전송하는 역할을 한다.

package ex02;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;


public class RateServer {
	private static float USD_RATE= 1124.70F;
	private static float JPY_RATE = 10.113F;
	private static float CNY_RATE = 163.30F;
	private static float GBP_RATE = 1444.35F;
	private static float EUR_RATE = 1295.97F;
	
	public static void main(String[] args){
		InputStream is;
		BufferedReader br;
		BufferedWriter bw;
		PrintWriter pw=null;
		OutputStream os;
		ServerSocket serverSocket;
		Socket s1=null;
		String ipAddrs=null;
		String inMessage=null;
		float outMessage=0f;
		try{
			serverSocket= new ServerSocket(5434);
			System.out.println("서버 실행 중...");
			
			while(true){
				//클라이언트의 접속을 인지 시에 accept()메소드를 호출해서 소켓 객체를 생성한다.
				s1= serverSocket.accept();
				is=s1.getInputStream();
				os = s1.getOutputStream();
				br=new BufferedReader(new InputStreamReader(is));
				String data=br.readLine();
				System.out.println("서버 수신 데이터:"+data);
				String result=calculate(data);
				System.out.println(result);
				
				
				bw = new BufferedWriter(new OutputStreamWriter(os));
				pw=new PrintWriter(bw,true);
				pw.println(result);
				pw.close();
			}
		}catch(IOException ie){
			ie.printStackTrace();
		}
	}
	
	private static  String calculate(String data){
		String []token=data.split(",");
		
		float won=Float.parseFloat(token[0]);
		String operator=token[1];
		String result=null;
		if(operator.equals("달러")){
			result=String.format("%.6f",won/USD_RATE);
		}else if(operator.equals("엔화")){
			result=String.format("%.6f",won/JPY_RATE);
		}else if(operator.equals("위안")){
			result=String.format("%.6f",won/CNY_RATE);
		}else if(operator.equals("파운드")) {
			result=String.format("%.6f",won/GBP_RATE);
		}else if(operator.equals("유로")) {
			result=String.format("%.6f",won/EUR_RATE);
		}
		
		return result;
		
	}	
}

* 다음은 클라이언트가 담당하는 기능을 구현한 소스이다.

package ex02;

import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;


public class RateClient extends JFrame{
	JLabel title = new JLabel("원화");
	JTextField operand1 = new JTextField(10);
	String[] opExpression = {"선택","달러", "엔화", "위안","파운드","유로"};
	JComboBox<String> opSelection = new JComboBox<String>(opExpression);
	JTextField txtResult = new JTextField(10);
	JButton btnClear = new JButton("다시입력");
	
	public RateClient() {
		Container contentPane = this.getContentPane();
		contentPane.setLayout(new FlowLayout());
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.pack();
		this.setVisible(true);
	}

	private void startFrame(){
		opSelection.addActionListener(new SelectionHandler());
		btnClear.addActionListener(new SelectionHandler());
		this.setTitle("클라이언트 프로그램");
		this.add(operand1);
		this.add(opSelection);
		this.add(txtResult);
		this.add(btnClear);
		this.setSize(700,200);
		
	}
	
	class SelectionHandler implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			if(e.getSource()==opSelection){
				calculate();
			}else if(e.getSource()==btnClear){
			  init();
			}
			
		}
	}
	
	private void init(){
		operand1.setText("");
		txtResult.setText("");
	}
	private void calculate(){
		float won=Integer.parseInt(operand1.getText());
		String result=null;
		String operator=opSelection.getSelectedItem().toString();
		
		 InputStream is;
		 BufferedReader br;
		 BufferedWriter bw;
		 OutputStream os;
		 PrintWriter pw=null;
		 
		try{
			Socket s1=new Socket("127.0.0.1",5434);
			os = s1.getOutputStream();
			is=s1.getInputStream();
			System.out.println("전송데이터:"+won+","+operator);
			
			
			bw = new BufferedWriter(new OutputStreamWriter(os));
			pw=new PrintWriter(bw,true);
		    pw.println(won+","+operator);
		    
		    br=new BufferedReader(new InputStreamReader(is));
			result=br.readLine();
			System.out.println("클라이언트 수신 데이터:"+result);
			txtResult.setText(result);
			s1.close();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args){
		RateClient calc=new RateClient();
		calc.startFrame();
		
	}	
}

* 클라이언트-서버 기반 프로그램은 기능(로직)이 변경되어도 모두 서버에서 처리하므로 클라이언트 프로그램을 수정할 필요가 없다. 중요한 기능은 서버에서 처리하므로 클라이언트 PC 기반 프로그램보다 데이터 보안 측면에서도 훨씬 우수하다.

- 하지만 클라이언트-서버 기반 프로그램에도 한계가 있다. 지금처럼 환율 계산 기능에 파운드와 유료 환율 변환 기능만 단순히 추가하는 경우라면 서버에서 기능을 쉽게 추가할 수 있지만 여기서 끝이 아니라, 사용자가 파운드와 유로를 선택할 수 있도록 셀렉트 박스에 '파운드'와 '유로' 항목을 추가해서 보여주어야 한다. 즉, 로직 뿐만 아니라 클라이언트 프로그램도 수정해야 한다.

- 물론 처음에 살펴본 클라이언트 PC 기반 프로그램보다 데이터 처리 관점에서는 훨씬 좋아졌지만, 역시 프로그램 화면 변경 시에는 여전히 추가 작업이 필요하다.


3. 웹 기반 프로그램 동작 방식

* 웹 기반 프로그램의 경우 클라이언트는 자신이 사용하는 클라이언트 프로그램을 직접 설치하는 것이 아니라 사용자 컴퓨터의 웹 브라우저를 통해 화면에 해당하는 HTML 문서를 서버에 요청한다. 그러면 서버에서는 요청 받은 HTML 문서를 브라우저에 전송하여 해당 기능을 담당하는 화면을 보여준다.

* 즉, 웹 기반 프로그램의 경우 사용자가 사용하는 프로그램의 기능이나 화면이 바뀌면 서버에서 모두 처리한다. 클라이언트-서버 기반 프로그램과 마찬가지로 클라이언트가 특별히 수행할 작업은 없다. 그리고 모든 기능을 서버에서 처리하므로 보안 면에서도 월등히 우수하다.