HTML 이란?

- HTML은 월드 와이드 웹(World Wide Web) 문서를 작성하는 Hyper Text Markup Language 이다.

Hyper Text : 한 문서 안의 특정 단어나 그림에  다른 문서가 연결된 형태 

Hyper Link : 특정 단어나 그림에 또 다른 문서나 그림을 꼬리에 꼬리를 물고 연결하는 것

- 웹 브라우저를 통하여 사용자에게 보여지는 문서의 내부 형식을 규정하는 언어이다.

- 제목, 본문, 목록, 링크, 이미지 등 다양한 컨텐츠를 의미 있게 마크업 할 수 있다.

- 웹 브라우저는 HTML태그를 읽고 분석하여 표현 내용을 화면에 나타낸다. 이로 인해 서로 다른 웹 브라우저라도 사용자에게는 동일한 형태의 웹 페이지를 표시할 수 있다.

- HTML 문서는 *.html, *.htm 등의 확장자를 사용한다.

 

Markup Language 

- 마크업 언어는 태그(tag) 등을 이용하여 문서나 데이터의 구조를 명기하는 언어의 한 가지이다.

- 태그는 원래 텍스트와는 별도로 원고의 교정부호와 주석을 표현하기 위한 것이었으나 용도가 점차 확장되어 문서의 구조를 표현하는 역할을 하게 되었다. 이러한 태그 방법의 체계를 마크업 언어라 한다.

- Markup은 문장, 그림, 표 배치, 폰트의 모양 및 크기, 들여쓰기, 줄 간격, 여백 등에 대한 정보를 의미한다.

 

웹 브라우저(Web Browser)

- HTML 문서와 그림, 멀티미디어 파일 등 월드 와이드 웹을 기반으로 한 인터넷 컨텐츠를 검색 및 열람하기 위한 응용 프로그램이다.

 

웹 페이지는 HTML, Javascript, CSS 의 3요소로 구성된다.

- HTML

웹 페이지상에서 문단, 제목, 표, 이미지, 동영상 등을 정의하고 그 구조와 의미를 부여하는 마크업 언어이다.

표준화된 태그로 웹 페이지를 작성한다. <html>, <head>, <body>, <img>, <a>, <table>, <div> 태그 등

 

- Javascript

객체 기반의 스크립트 프로그래밍 언어로 웹 브라우저 내에서 주로 사용하며, 다른 응용 프로그램의 내장 객체에도 접근할 수 있는 기능을 가지고 있다.

Node.js와 같은 런타임 환경과 같이 서버 사이드 네트워크 프로그래밍에도 사용되고 있다.

동적으로 컨텐츠를 바꾸거나 멀티미디어를 다루고, 움직이는 이미지 등 웹 페이지를 꾸며주도록 하는 프로그래밍 언어이다.

 

- CSS(Cascading Style Sheets)

CSS는 HTML 문서 내에 서체의 종류, 크기, 색, 여백 등을 지정하여 사용자의 web brower 환경에 상관 없이 일정한 화면을 보여주는 기능이다.

 

HTML 문서의 기본 형식

- DTD(Document Type Definition) 선언 : 문서의 첫 줄에 DTD 선언하며, DTD는 정의하는 문서에서 사용되는 언어와 버전 등을 지정한다.

- <html>~</html> : HTML 문서의 최상위 요소로 모든 HTML 문서는 하나의 <html> 요소를 갖는다.

- <head>~</head> : HTML 문서의 메타데이터(metadata)를 정의하며, 메타데이터는 HTML 문서에 대한 정보를 의미한다. 메타데이터는 <title>, <style>, <meta>, <link>, <script>, <base>태그 등을 이용하여 표현한다.

- <body>~</body> : 웹 브라우저를 통해 보이는 내용(content) 부분을 나타낸다.

 

태그(tag)의 기본 구성

- 요소 (element) : 시작과 종료를 나타내는 태그로 이루어진 모든 명령

- 태그 '<'과 '>'으로 둘러 쌓인 요소의 일부로 시작 태그(<tag>)와 종료 태그(</tag>)로 이루어져 있지만 종료 태그가 없는 태그도 있다.

- 속성(attribute) : 요소의 시작 태그 내에서 사용하며 명령어를 구체화 시키는 역할을 한다.

- 속성 값 : 속성과 관련된 값

- 태그와 속성은 대소문자를 구분하지 않는다.

 

<!DOCTYPE> 태그 >> 

더보기

- 문서 정의 태그(Document Type Definition)

- 문서에서 사용되는 언어와 버전을 지정하는 데 사용한다. 즉, HTML이 어떤 버전으로 작성되었는지 미리 선언해 웹 브라우저가 내용을 올바로 표시할 수 있도록 해준다.

- <! DOCTYPE> 태그는 모든 HTML문서의 최 상단에 기술한다.

 

DTD(Document Type Definition)

문서 형식 정의(DTD)는 페이지를 불러오기 위한 기본이 되는 문서타입을 정하는 것으로 브라우저에 현재의 웹 페이지가 어떤 문서 형식을 가지는지 전달해준다.

- 호환모드(Transitional Mode) : Transitional//EN : 생략해도 실행하는데 지장이 없는 트랜지셔널 모드를 말한다.

- 엄격모드(Strict Mode) : Strict//EN : 생략을 허용하지 않는 xml같이 올바른 형식의 문서(Well formed Document)를 의미한다.

이번에는 Java를 Orcale에 연동한 JDBC를 이용해서 Semi Project를 진행했다.

0901~0910까지 진행했으며 10일에 발표를 했다.

 

ERD

 

회원가입, 회원주문

더보기
회원가입 예시
회원주문

현재 주문과 동시에 포인트를 넣어주고 있음...(소스에서)

 나중에 배송이 완료된 이후(지금은 이것까지는 안했긴 함...) 배송일을 입력 한 후에 포인트를 넣던가 방식을 바꿔야 될 것 같다.

회원주문리스트

 

후기작성

>> 후기작성의 경우 도착일이 생긴 후에 작성하게끔 하려면 어떻게 해야될까 ?

후기작성할 때 빵코드를 입력받지 않고 후기작성하려면 ? >> 주문번호를 받아서 먼저 주문번호에 해당하는 결과를 받은 후에 별점 내용을 거기에 넣어서 해줘야될 듯 ..

 

내 정보

아직 상품준비중인데 포인트가 쌓였음. 이부분도 나중에는 생각해봐야할 문제

재고입고

더보기

아까 민트초코마카롱을 5개를 주문했는데 10개가 사라져있었다. 소스를 뜯어보니, 

재고를 변경하는 곳에 문제가 있었다.

빵코드를 입력받아 재고는 빵코드로 합쳐서 보여지는데, 

재고 업데이트를 빵코드를 받아서 하다보니까 빵코드에 해당하는 재고는 입고번호에 따라 계속 넣어지는데, (즉 빵코드에 해당하는 자료가 입고받을 때마다 넣어지므로 여러개) 재고를 2번을 입고 받았었기 때문에 해당 빵코드의 qty에서 -5가 다 들어가기 때문에 2번의 입고 정보에 다 -5가 되어서 -10이 되었던 것이다.

 

납품업체 리스트

납품업체 리스트 확인.

납품업체 하나에 (부모테이블) 입고(자식테이블) 이 여러개가 존재할 수 있다. 참조키로 납품업체 코드를 해놨다. 그래서 납품업체 삭제는 거의 사용하지 않을 듯. 입고를 한번이라도 했으면 삭제해도 자식 레코드는 재고를 위해서 사용해야되서.

 

입고1
입고2

 

입고 후 재고확인

입고날짜를 넣어줄때는 매개변수가 없는 메소드를 통해 현재 날짜를 넣어주고,

데이터베이스에서 가져올 때는 매개변수가 있는 메소드를 통해 데이터베이스에서 가져온 날짜로 set할 수 있도록 overloading 을 통해 구현했다. 

입고번호도 overloading을 통해서 입고에서 넣어줄 때는 오늘날짜+랜덤번호 를 통해 넣었다.

데이터베이스에서 가져와서 데이터를 출력할 때는 가져온 값을 매개변수로 해서 넣어준다.

 

유통기한 조회, 폐기등록

더보기
폐기 전 재고
폐기
폐기 후 재고

폐기가 고구마케익만 되었다. 소스를 보고 해당 재고를 다 없애도록 고쳐야 할듯...

폐기 후 재고

하나씩 만 폐기되도록 되어있음. 한번 더 실행하니까 이번엔 뉴욕치즈케익이 사라졌음.

싹 다 사라지도록 변경해야될 것 같다.

배송일자 등록

더보기
배송등록 전 주문리스트

주문총금액부분은 없애야함.

배송등록

해당 주문번호에 배송일자를 추가한다.

배송등록 후 주문리스트
새로운 주문 확인

총 주문리스트 말고 새로운 주문만 확인하는 리스트도 있다.

배송등록 후 주문 확인 리스트

배송등록 후에는 주문 확인 리스트가 뜨지 않도록 설정했다.

비회원주문, 비회원주문확인

더보기
비회원 구매
비회원 구매
비회원구매목록확인

 

후기확인

 

판매현황

더보기
회원 판매현황

총팔린금액에 개당 금액이 들어가있다. 고쳐야함!

비회원 판매현황

여기도 총금액에 개당금액이 들어가있다.

 

 

수정이 필요한 부분은 짬짬히 따로 수정해가며 완성도를 높여보자!!

10일 남짓 시간동안 요구사항분석, 시나리오작성, 추상화, 코딩, 함께해준 팀원들에게 모두 감사합니다:)

 

CharForm >>

더보기
package netEx;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ChatForm extends JFrame implements ActionListener, Runnable {
	private static final long serialVersionUID = 1L;

	private JTextField tf = new JTextField();
	private JTextArea ta = new JTextArea();

	public ChatForm() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		ta.setEditable(false);
		JScrollPane sp = new JScrollPane(ta);
		add(sp, BorderLayout.CENTER);
		
		// JtextField에서 엔터를 누르면 ActionEvent가 발생되므로
		// 		ActionEvent를 등록하여 ActionEvent 처리
		tf.addActionListener(this);
		add(tf, BorderLayout.SOUTH);

		setTitle("채팅");
		setSize(500, 550);
		setVisible(true);

		tf.requestFocus();

	}

	public static void main(String[] args) {
		new ChatForm();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == tf) {
			String s = tf.getText().trim();
			if (s.length() == 0)
				return;

			try {

				ta.append("보냄] " + s + "\n");

				tf.setText("");
				tf.requestFocus(); // 포커스를 JTextField 에
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}

	}

	@Override
	public void run() {
		// TODO Auto-generated method stub

	}
}

 

ChatClient >>

더보기
package netEx;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ChatClient extends JFrame implements ActionListener, Runnable {
	private static final long serialVersionUID = 1L;

	private JTextField tf = new JTextField();
	private JTextArea ta = new JTextArea();

	private Socket sc = null;
	private String host = "127.0.0.1";
	private int port = 8000;
	private String nickName = "자바";

	public ChatClient() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		ta.setEditable(false);
		JScrollPane sp = new JScrollPane(ta);
		add(sp, BorderLayout.CENTER);

		// JtextField에서 엔터를 누르면 ActionEvent가 발생되므로
		// ActionEvent를 등록하여 ActionEvent 처리
		tf.addActionListener(this);
		add(tf, BorderLayout.SOUTH);

		setTitle("채팅-클라이언트");
		setSize(500, 550);
		setVisible(true);

		tf.requestFocus();

	}

	public void connect() {
		// 서버에 연결
		try {
			sc = new Socket(host, port);
			ta.setText("서버에 접속...\n");

			Thread t = new Thread(this);
			t.start();

		} catch (IOException e) {
			// 서버가 실행되지 않은 경우
			sc = null;
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		new ChatClient().connect();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == tf) {
			String s = tf.getText().trim();
			if (s.length() == 0)
				return;

			try {
				if (sc == null) {
					return;
				}

				// 서버에 정보를 전송하기 위한 출력 스트림
				PrintStream out = new PrintStream(sc.getOutputStream());
				out.println(nickName + "] " + s); // 서버에게 문자열 전송

				ta.append("보냄] " + s + "\n");
				ta.setCaretPosition(ta.getDocument().getLength());;
				
				tf.setText("");
				tf.requestFocus(); // 포커스를 JTextField 에
			} catch (IOException e2) {
				ta.append("서버가 종료 됨 !!!\n");
				sc = null;
				// e2.printStackTrace();
			}
		}

	}

	@Override
	public void run() {
		String s;

		try {
			if (sc == null) {
				return;
			}

			// 서버가 보낸 정보를 받기 위한 입력 스트림
			BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));

			while((s = br.readLine())!=null) {
				ta.append(s+"\n");
				ta.setCaretPosition(ta.getDocument().getLength());;
			}
		} catch (IOException e) {
			s = "서버가 종료 되었습니다.\n";
			ta.append(s);
			sc = null;
			// e.printStackTrace();
		}

	}
}

 

ChatServer >>

더보기
package netEx;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

// 1:1 채팅
public class ChatServer extends JFrame implements ActionListener, Runnable {
	private static final long serialVersionUID = 1L;

	private JTextField tf = new JTextField();
	private JTextArea ta = new JTextArea();

	private ServerSocket ss = null;
	private int port = 8000;
	private Socket sc = null;

	public ChatServer() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		ta.setEditable(false);
		JScrollPane sp = new JScrollPane(ta);
		add(sp, BorderLayout.CENTER);

		tf.addActionListener(this);
		add(tf, BorderLayout.SOUTH);

		setTitle("채팅-서버");
		setSize(500, 550);
		setVisible(true);

		tf.requestFocus();
	}

	public void serverStart() {
		try {
			ss = new ServerSocket(port);
			ta.setText("서버 시작 !!!\n");
			sc = ss.accept(); // 클라이언트의 접속을 기다린다.
			// 클라이언트가 접속하면 클라이언트와 통신을 할 수 있는 Socket 객체를 반환 받는다.

			Thread t = new Thread(this);
			t.start();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		new ChatServer().serverStart();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == tf) {
			String s = tf.getText().trim();
			if (s.length() == 0)
				return;

			try {
				if (sc == null) {
					return;
				}

				// 클라이언트에게 정보를 전송 한다.
				PrintStream out = new PrintStream(sc.getOutputStream());
				// OutputStream을 PrintStream으로 변환
				out.println("서버] " + s);

				ta.append("보냄] " + s + "\n");
				ta.setCaretPosition(ta.getDocument().getLength());;

				tf.setText("");
				tf.requestFocus(); // 포커스를 JTextField 에
			} catch (IOException e2) {
				// e2.printStackTrace();
				ta.append("클라이언트가 접속을 해제 했습니다.\n");
				sc = null;
			}
		}

	}

	@Override
	public void run() {
		String str;
		String ip = "";

		if (sc == null) {
			return;
		}

		try {
			// 접속한 클라이언트가 보낸 정보를 입력 스트림으로 받는다.
			BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));
			
			// 접속한 클라이언트의 ip 주소
			ip = sc.getInetAddress().getHostAddress();
			ta.append("["+ip+"] 접속 ...\n");
			
			// 클라이언트가 보낸 정보를 받는다.
			while((str = br.readLine()) !=null ) {
				ta.append(str+"\n");
				ta.setCaretPosition(ta.getDocument().getLength());;
			}
			
		} catch (IOException e) {
			str = "["+ip+"] 접속 해제 !!!";
			ta.append(str+"\n");
			sc = null;
		}
	}
}

 

ChatServer2 >>

더보기
package netEx;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;

// 1:다 채팅 프로그램
public class ChatServer2 {
	// 접속한 클라이언트를 저장하기 위한 벡터 객체
	private Vector<Socket> client = new Vector<>();
	private ServerSocket ss = null;
	private int port = 8000;

	// 내부 스레드 클래스
	class WorkerThread extends Thread {
		private Socket sc = null;

		public WorkerThread(Socket sc) {
			this.sc = sc;
		}

		@Override
		public void run() {
			String ip= null;
			String msg;
			
			try {
				// 클라이언트가 보낸 정보를 받기 위한 입력 스트림
				BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));
				ip = sc.getInetAddress().getHostAddress();
				
				// 접속한 클라이언트 소켓을 벡터에 저장
				client.add(sc);
				
				// 다른 클라이언트에게 접속 사실을 알림
				msg = "["+ip+"] 에서 접속 !!!";
				sendMsg(msg);
				
				System.out.println(msg);
				
				// 클라이언트가 보낸 정보를 받아 다른 클라이언트에게 보냄
				while((msg = br.readLine()) != null) {
					sendMsg(msg);
				}
				
			} catch (IOException e) {
				// 클라이언트가 채팅방을 나간 경우
				msg = "["+ip+"] 퇴장...";
				sendMsg(msg);
				
				client.remove(sc);
				sc = null;
				System.out.println(msg);
			}
		}
		
		// 다른 모든 클라이언트에게 메세지 전송
		public void sendMsg(String msg) {
			for(Socket s : client) {
				try {
					if( s == sc) {
						continue; // 자기 자신
					}
					
					PrintStream out = new PrintStream(s.getOutputStream());
					out.println(msg);
				} catch (Exception e) {
				}
			}
		}
	}

	public void serverStart() {
		try {
			ss = new ServerSocket(port);
			System.out.println("서버 시작 !!!");

			while (true) {
				// 클라이언트 접속을 기다린다.
				Socket sc = ss.accept();

				// 클라이언트가 접속하면 클라이언트가 보낸 정보를 받을 스레드 객체를 생성하고 스레드를 실행한다.
				WorkerThread t = new WorkerThread(sc);
				t.start();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		ChatServer2 obj = new ChatServer2();
		obj.serverStart();
	}

}

 

 

네트워크 프로그래밍

java.net.InetAddress 클래스

- InetAddress 클래스는 인터넷상의 IP 주소를 객체 모델링한 클래스로 IP주소와 관련된 여러 정보를 제공하는 클래스이다.

 

Ex001_InetAddress >>

더보기
package netEx;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetAddress;

public class Ex001_InetAddress {

	public static void main(String[] args) {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String host = null;
		
		try {
			System.out.print("호스트명[www.naver.com 또는 컴이름] ? ");
			host = br.readLine();
			
			InetAddress ia = InetAddress.getByName(host);
			
			String str = ia.getHostName();
			String ip = ia.getHostAddress();
			
			System.out.println("호스트명 : "+str);
			System.out.println("아이디 : "+ip);
			
		} catch (Exception e) {
			e.printStackTrace();
		} 

	}

}

위 예제를 통해 InetAddress 클래스로 해당 주소의 호스트명과 ip를 가져올 수 있었다.

 


URL(Uniform Resource Locator) 이란?

- URL은 네트워크 상에서 자원이 어디 있는지를 알려주기 위한 규약이다.

- URL은 웹 사이트 주소뿐만 아니라 컴퓨터 네트워크 상의 자원을 모두 나타낼 수 있다.

- URL 구성

 프로토콜://호스트:[포트번호]/[파일]#[세션]

 

java.net.URL 클래스

- URL 클래스는 웹(World Wide Web) 상에 존재하는 자원(resource)에 접근하기 위해, 자원의 유일한 주소를 나타내기 위한 기능을 제공해 주는 클래스이다.

- 자원이란 파일 또는 디렉토리와 같은 간단한 것에서부터 데이터베이스 또는 서치엔진에 대한 쿼리(query)와 같은 복잡한 형태의 객체일 수도 있다.

 

java.net.URLConnection 클래스

- URLConnection 클래스는 추상 클래스로 애플리케이션과 URL 간의 통신 링크를 위한 모든 클래스들에 대한 상위 클래스이다.

- URLConnection 객체는 URL을 이용하여 참조된 자원에 대해 읽고 쓰는 작업을 할 수 있도록 해준다.

 

URLEncoder 와 URLDecoder 클래스

- application/x-www-form-urlencoded 는 HTML form 데이터 기본 전송 방식이다.

영문자 숫자('a'~'z', 'A'~'Z', '0'~'9')는 그대로 표현된다.

특수 문자의 '.'. '-', '*', '_' 는 그대로 표현된다.

공백 문자(' ')는 '+' 기호로 변환된다.

기타 문자는 %xy와 같이 세 개의 문자로 변환되는데, 이 때 xy는 문자의 하위 8비트에 대한 16진수 두자리를 나타낸다.

- MIME(Multipurpose Internet Mail Extensions) type : 메세지 컨텐트 형식을 정의하기 위한 인터넷 표준을 의미한다.

 

java.net.URLEncoder 클래스

- HTML 형식을 encode하기 위한 유틸리티 클래스로 문자열을 application/x-www-form-urlencoded MIME 형식으로 변환하기 위한 static 메소드를 제공한다.

 

java.net.URLDecoder 클래스

- HTML 형식을 디코드하기 위한 유틸리티 클래스로, applicaion/x-www-form-urlencoded MIME 형식을 문자열로 디코드 하기위한 static 메소드를 제공한다.

 

Ex002_URL >>

더보기
package netEx;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

public class Ex002_URL {

	public static void main(String[] args) {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String webUrl, s;

		BufferedReader nbr = null;

		try {
			System.out.print("웹주소[https://www.naver.com] ? ");
			webUrl = br.readLine();
			
			// URL : 웹(World Wide Web)상의 리소스에 대한 포인터를 나타냄
			URL url = new URL(webUrl);
			InputStream is = url.openStream();
			
			nbr = new BufferedReader(new InputStreamReader(is, "UTF-8"));
			
			while( (s = nbr.readLine()) !=null) {
				System.out.println(s);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (nbr != null) {
				try {
					nbr.close();
				} catch (Exception e2) {
				}
			}
		}

	}

}

아직 뭔지 모르는 소스들을 가져왔다.

 

Ex003_URLEncoder >>

더보기
package netEx;

import java.net.URLDecoder;
import java.net.URLEncoder;

public class Ex003_URLEncoder {

	public static void main(String[] args) {
		String s1, s2;
		s1 = "자바 java";
		
		try {
			// 인터넷에서 데이터를 전송할 수 있는 상태로 변환(application/x-www-form-urlencoded)
			s2 = URLEncoder.encode(s1, "UTF-8");
			System.out.println(s2); // %EC%9E%90%EB%B0%94+java

			// 주소형식으로 변환된 문자열을 다시 원래대로 변환
			s1 = URLDecoder.decode(s2, "UTF-8");
			System.out.println(s1);
			
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

URLEncoder 와 URLDecoder 클래스로 문자열을 인터넷에서 주고받을 수 있는 형태로 변환해보고 다시 변환된 것을 우리가 볼 수 있도록 바꾸어보았다.

 

동기화 ?

더보기

동기화란?

- 멀티 스레드(Multi Thread) 환경에서는 여러 스레드가 동일한 프로세스 내의 자원을 공유해서 사용하기 때문에 문제점이 발생할 수 있다.

- 따라서 공유 자원에 여러 개의 스레드가 동시에 접근하지 못하도록 설정하여야 하는데 이를 동기화라 한다.

- 즉, 한 스레드가 동기화된 객체를 사용하는 경우 오직 그 스레드 만이 그 객체에 접근할 수 있도록 한다.

 

크리티컬 섹션(Critical Section, 임계 영역)

공유 데이터나 리소스(메모리나 파일)에 대한 접근을 하나의 스레드로 제한한다. 즉, 동일한 프로세스 내의 스레드 간 동기화에 사용한다.

뮤텍스(Mutex) 

크리티컬 섹션과 동일한 기능을 가지지만 차이가 있다면 다른 프로세스에 속한 스레드를 제어할 수 있다.

세마포어(Semaphore) 

뮤텍스와 유사하지만 동시에 수행할 수 있는 스레드를 여러 개로 제한할 수 있다.

모니터(Monitors) 

두 개의 스레드에 의해 공유되고 그 값이 참조될 때 반드시 동기화되어야 하는 공유 데이터와 같은 객체를 상태변수(condition variables)라고 한다. 자바 언어에서는 상태 변수에 대한 모니터를 사용할 수 있도록 함으로써 스레드를 동기화 한다. 모니터는 자신이 소유한 스레드를 제외한 다른 스레드의 접근을 막는다.

교착 상태(DeadLock)

두 개 이상의 작업이 서로 상대방의 작업이 끝나기만을 기다리고 있기 때문에 결과적으로 아무 것도 완료되지 못하는 상태를 가리킨다.

 

Synchronized 키워드

- Synchronized 키워드를 통해 해당 작업과 관련된 공유 데이터에 lock을 걸어서 먼저 작업 중이던 스레드가 작업을 완전히 마칠 때까지는 다른 스레드에게 제어권이 넘어가더라도 데이터가 변경되지 않도록 보호함으로써 스레드의 동기화를 가능하게 한다.

Ex001, Ex003 : 동기화하지 않은 경우

더보기
package threadEx2;

public class Ex001_Synchronized {
	public static void main(String[] args) {
		MyBank1 b = new MyBank1();
				
		Thread t1 = new Thread(b);
		Thread t2 = new Thread(b);
		
		t1.start();
		t2.start();
	}
}

// 동기화 하지 않은 경우
class MyBank1 implements Runnable {
	private int money = 10000;
	
	
	@Override
	public void run() {
		int need = 6000;
		int n = 0;
		String msg = null;
		
		try {
			if(getMoney()>=need) {
				Thread.sleep(200);
				n = drawMoney(need);
				msg = "인출 성공";
			} else {
				n = 0;
				msg = "인증 실패";
			}
			
			System.out.println(msg + ", 인출금액:"+n+", 잔고:"+getMoney());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}


	public int getMoney() {
		return money;
	}


	private int drawMoney(int m) {
		money -= m;
		return m;
	}
}
package threadEx2;

public class Ex003_Synchronized {
	public static void main(String[] args) {
		ShareData1 share = new ShareData1();
		
		UpThread1 t1 = new UpThread1(share, "up");
		DownThread1 t2 = new DownThread1(share, "down");
		
		t1.start();
		t2.start();
		
	}
}

// 동기화 하지 않은 경우
class ShareData1 {
	private int num = 100;
	
	public void up(String title) {
		System.out.print(title+":"+num);
		num++;
		System.out.println(", 증가->"+num);
	}
	public void down(String title) {
		System.out.print(title+":"+num);
		num--;
		System.out.println(", 감소->"+num);
	}
}

class UpThread1 extends Thread {
	private ShareData1 share;
	private String title;
	
	public UpThread1(ShareData1 share, String title) {
		this.share = share;
		this.title = title;
	}
	
	public void run() {
		for(int i=0; i<5; i++) {
			try {
				sleep(500);
				share.up(title);
			} catch (Exception e) {
			}
		}
	}
}

class DownThread1 extends Thread {
	private ShareData1 share;
	private String title;
	
	public DownThread1(ShareData1 share, String title) {
		this.share = share;
		this.title = title;
	}
	
	public void run() {
		for(int i=0; i<5; i++) {
			try {
				sleep(500);
				share.down(title);
			} catch (Exception e) {
			}
		}
	}
}

Ex002, Ex004 : 동기화한 경우

더보기
package threadEx2;

public class Ex002_Synchronized {
	public static void main(String[] args) {
		MyBank2 b = new MyBank2();
				
		Thread t1 = new Thread(b);
		Thread t2 = new Thread(b);
		
		t1.start();
		t2.start();
	}
}

// 동기화 한 경우
class MyBank2 implements Runnable {
	private int money = 10000;
	
	
	@Override
	public void run() {
		int need = 6000;
		int n = 0;
		String msg = null;
		
		try {
			synchronized (this) { // 동기화 블럭
				if(getMoney()>=need) {
					Thread.sleep(200);
					n = drawMoney(need);
					msg = "인출 성공";
				} else {
					n = 0;
					msg = "인증 실패";
				}				
			}
			
			System.out.println(msg + ", 인출금액:"+n+", 잔고:"+getMoney());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}


	public int getMoney() {
		return money;
	}


	private int drawMoney(int m) {
		money -= m;
		return m;
	}
}

Ex005_wait : 대기

더보기
package threadEx2;

public class Ex005_wait {
	public static void main(String[] args) {
		MyBank3 atm = new MyBank3();
		
		Thread t1 = new Thread(atm, "t1");
		Thread t2 = new Thread(atm, "t2");
		
		t1.start();
		t2.start();
		
	}
}

class MyBank3 implements Runnable {
	private long money = 10000;
	
	public void run() {
		synchronized (this) {
			for(int i = 0; i < 10; i++) {
				if(getMoney() <= 0) {
					// wait()에 의해 대기하고 있는 모든 스레드를 깨운다.
					this.notifyAll();
					break;
				}
				
				drawMoney(1000);
				
				if(getMoney()>=2000 && getMoney() % 2000 == 0) {
					try {
						// wait()를 만나면 해당 쓰레드는 해당 객체의 모니터링 락에 대한 권한을 가지고 있으면
						// 모니터링 락의 권한을 놓고 대기한다.
						// synchronized 블록에서만 사용
						this.wait();
					} catch (Exception e) {
					}
				} else {
					// wait()에 의해 대기하고 있는 해당 스레드를 깨운다.
					// 아래 notify()를 주석 처리하면 무한 대기(deadlock)상태가 된다.
					this.notify();
				}
				
			}
			
		}
	}

	public long getMoney() {
		return money;
	}
	
	public void drawMoney(long m) {
		System.out.print(Thread.currentThread().getName()+", "); // 현재 쓰레드명 출력
		
		if(getMoney()>=m) {
			money -= m;
			System.out.printf("잔액 :  %,d원\n", getMoney());
		} else {
			System.out.println("잔액이 부족 합니다.");
		}
	}
}

 

스레드 우선순위

더보기

- 자바의 스레드 스케쥴링은 우선순위(Priority)방식과 라운드 로빈(Round-robin) 방식을 사용한다.

1) 우선순위(Priority) 방식

- 우선순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케쥴링 하는 것

- 우선순위를 부여하여 코드로 제어가 가능하다.

2) 라운드 로빈(Round-robin) 방식

- 시간 할당량(Time Slice)을 정해서 스레드를 정해진 시간만큼 실행시키고 다른 스레드를 실행하는 방식

- JVM에 의해 정해지므로 코드로 제어할 수 없다.

 

- 스레드는 우선순위를 나타내는 필드(priority)를 가지고 있다. 1~10 사이의 값을 갖는다. (10: 가장 높음)

- 자바 런타임 스케줄러는 현재 실행 가능한 스레드들 중에서 우선순위가 가장 높은 스레드를 실행시켜 주는데, 현재 실행중인 스레드보다 더 높은 우선순위를 갖는 스레드가 실행가능 상태가 되면 자바 런타임 스케줄러는 우선순위가 더 높은 스레드를 실행시켜 준다.

- 스레드 우선 순위는 스레드를 생성한 스레드로부터 상속 받는다. main() 메소드에서 스레드를 생성하면 main() 메소드의 우선 순위가 5이므로 자동으로 5를 갖는다.

Ex007_Priority >>

더보기
package threadEx;

public class Ex007_Priority {
	public static void main(String[] args) {
		MyThread7 t1 = new MyThread7("A");
		MyThread7 t2 = new MyThread7("B");
		MyThread7 t3 = new MyThread7("C");

		t1.setPriority(Thread.MIN_PRIORITY); // 1
		t2.setPriority(Thread.MAX_PRIORITY); // 10
		
		t1.start();
		t2.start();
		t3.start();
		
	}
}

class MyThread7 extends Thread {
	public MyThread7(String name) {
		super(name);
	}

	@Override
	public void run() {
		for (int i = 1; i <= 20; i++) {
			// System.out.println(getName() + " -> " + i);
			System.out.println(this); // [스레드명, 우선순위, 그룹]
		}

	}
}

몇 번 실행시켜보면 우선순위가 높은 B부터 일처리를 하는 것을 알 수 있다.

 

데몬 스레드(Daemon Thread)

- 일반 스레드는 자신의 작업을 수행하도록 되어 있는 반면, 데몬 스레드란 일반 스레드로 부터 요청을 받아 특정 서비스를 수행하는 작업을 한다.

- 즉, 다른 일반 스레드를 돕는 보조적인 역할을 하는 스레드이다.

- 일반 스레드가 모두 종료되면 데몬 스레드도 자동 종료된다.

- 데몬 스레드가 생성한 스레드는 자동으로 데몬 스레드가 된다.

- 예를 들어, 가비지 컬렉터, 워드 프로세스의 자동 저장 등 백그라운드에서 특별한 작업을 처리하는 용도로 사용한다.

 

Ex006_Daemon Thread  >>

더보기
package threadEx;

/*
 - 독립 스레드
 	메인은 프로그램의 시작점이지 종료점은 아니다. 
 	메인 스레드가 종료되어도 독립 스레드의 작업이 완료될 때까지 프로그램은 종료되지 않는다.
 - 데몬 스레드
 	다른 스레드에게 도움을 주기 위한 스레드
 	메인 스레드가 종료되면 데몬 스레기의 작업이 완료되지 않아도 프로그램은 종료됨.
 */
public class Ex006_Daemon {
	public static void main(String[] args) {
		MyThread6 t1 = new MyThread6();
		MyThread6 t2 = new MyThread6();
		MyThread6 t3 = new MyThread6();

		// 스레드들 데몬으로 만듦
		t1.setDaemon(true);
		t2.setDaemon(true);
		t3.setDaemon(true);
		
		t1.start();
		t2.start();
		t3.start();
		
		try {
			t1.join(); // 스레드가 종료될 때 까지 대기
			t2.join();
			t3.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("메인 종료...");
	}
}

class MyThread6 extends Thread {
	@Override
	public void run() {
		for (int i = 1; i <= 20; i++) {
			System.out.println(getName() + " -> " + i);
			
			try {
				Thread.sleep(500);
			} catch (Exception e) {
			}
		}
		
	}
}

메인이 종료되면 같이 종료된다.

Ex005_Runnable : 1초마다 찍는 스레드

더보기
package threadEx;

import java.util.Calendar;

public class Ex005_Runnable {

	public static void main(String[] args) {
		Thread t = new Thread(new MyThread5());
		t.start();

	}

}

class MyThread5 implements Runnable {

	@Override
	public void run() {
		String str;

		while (true) {
			Calendar cal = Calendar.getInstance();

			str = String.format("%tF %tT", cal, cal);
			System.out.println(str);

			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

}

Thread.sleep(1000); 괄호 안에는 millisecond 단위이므로 1000이면 1초를 뜻한다.

terminate를 누르지 않으면 계속 시간이 찍힌다.

Ex006_Runnable : 윈도우창을 활용한 1초마다 시간이 바뀌게 하는 예제 [JFrame 상속 후 Runnable 인터페이스 구현]

더보기
package threadEx;

import java.awt.BorderLayout;
import java.util.Calendar;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class Ex006_Runnable {
	public static void main(String[] args) {
		ChatForm cf = new ChatForm();
		Thread t = new Thread(cf);
		t.start();
	}
}

class ChatForm extends JFrame implements Runnable {
	private static final long serialVersionUID = -5166274042184939576L;
	
	private JTextField tf = new JTextField();
	private JTextArea ta = new JTextArea();
	
	public ChatForm() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		JScrollPane sp = new JScrollPane(ta);
		add(sp, BorderLayout.CENTER);
		
		add(tf, BorderLayout.SOUTH);
		
		setTitle("채팅...");
		setSize(500,500);
		setVisible(true);
		
		
	}

	@Override
	public void run() {
		String s;
		while(true) {
			try {
				Calendar cal = Calendar.getInstance();
				s = String.format("%tF %tT", cal, cal);
				setTitle(s);
			} catch (Exception e) {
			}
		}
		
	}
	
}

위 코드를 실행하면 이렇게 따로 윈도우창이 뜨고 창 제목이 시스템 시간에 따라 1초씩 찍히는 것을 확인할 수 있다. 

 

+ Recent posts