#1 교육정리
1) DAO 객체 도입
2) Client/Server 아키텍처로 전환
#2 DAO(Data Access Object)
DAO는 Data Access Object의 약자로, DB의 데이터에 접근하기 위한 객체를 가리킨다. DB에 접근하기 위한 로직을 분리하기 위해 사용한다. 직접 DB에 접근하여 data를 삽입, 삭제, 조회 등 조작할 수 있는 기능을 수행한다.
사용자는 자신이 필요한 Interface 를 DAO 에게 던지고 DAO는 이 Interface를 구현한 객체를 사용자에게 편리하게 사용 할 수 있도록 반환한다.
DAO는 데이터베이스와 연결할 Connectuion 까지 설정되어 있는 경우가 많습니다.
간단하게, DB에 접속하여 데이터의 CRUD(생성, 읽기, 갱신, 삭제) 작업을 시행하는 클래스이다.
JSP 및 Servlet 페이지 내에 로직을 기술하여 사용할 수 있지만, 코드의 간결화 및 모듈화, 유지보수 등의 목적을 위해 별도의 DAO 클래스를 생성하여 사용하는 것이 좋다.
한 줄 요약 : DAO는 DB를 사용하여 데이터의 조회 및 조작하는 기능을 전담하는 오브젝트이다.
다음은 수업 내용 정리는 수업 중 만든 Project에 DAO를 적용시킨 내용입니다.
DAO 를 적용시켜야 하는 이유)
현재는 위와 같이 App 클래스에서 데이터 파일 입출력을 다루고 있어 파일의 입출력 방식이 바뀔때마다 코드를 변경해줘야 한다는 번거로움이 있었습니다.
그리고 MemberListListener 클래스에서 데이터 조작을 함으로써 저장소가 변경되면 데이터 조작 방식이 바뀌기 때문에 추가적인 Listener 클래스들의 코드를 같이 변경해줘야 한다는 문제점이 있었습니다.
그래서 위와 같이 MemberDao 에서 데이터 파일 입출력과 데이터 조작을 다룸으로써 저장 방식이ㅣ 바뀌더라도 다른 Listener들이 영향을 받지 않도록 별도의 클래스로 분리하여 캡슐화 했습니다.
그렇다면 DAO는 어떻게 만들까요?)
아래와 같이 List의 방식대로 변경한다면 기존 프로젝트는 계속해서 그 방식으로 돌아가길 원합니다.
그렇기에 새로운 DAO 클래스를 생성해야 합니다.
DAO 만들기 결론1)
아래는 저장 방식 변경에 맞추어 DAO 클래스를 새로 정의하는 방법입니다.
DAO 만들기 결론2)
아래 방법은 Listener 와 DAO를 인터페이스로 연결하여 DAO 클래스가 변경되더라도 Listener 클래스는 변경할 필요가 없도록 한 방법입니다.
코드가 궁금하시다면 아래 github를 참조해주세요.
https://github.com/wooki37/bitcamp-study (-> myapp/app/src/main/java/bitcamp/myapp/)
GitHub - wooki37/bitcamp-study: 비트캠프 네이버 클라우드과정 훈련 소스
비트캠프 네이버 클라우드과정 훈련 소스. Contribute to wooki37/bitcamp-study development by creating an account on GitHub.
github.com
#3 Client/Server 아키텍처로 전환
Java의 정석을 읽고 정리하신 내용이 공부하는데 도움 되어 아래와 같이 정리해봅니다.
참조 : https://haeng-on.tistory.com/43
1. 네트워킹
(1) 자바에서 제공하는 java.net 패키지를 사용하면 네트워크 어플리케이션의 데이터 통신 부분을 쉽게 작성할 수 있다.
(2) 서버는 서비스를 제공하는 컴퓨터(service provider)이고 클라이언트는 서비스를 사용하는 컴퓨터(service user)이다.
(3) 서비스는 서버가 클라이언트로부터 요청받은 작업을 처리하며 그 결과를 제공하는 것. 서버가 제공하는 서비스의 종류에 따라 파일 서버, 메일서버, 어플리케이션 서버 등이 있다.
(4) 네트워크를 구성할 때 전용서버를 두는 것을 서버기반모델(server-based model)이라 하고 별도의 전용서버 없이 각 클라이언트가 서버역할을 동시에 수행하는 것을 P2P모델(peer-to-peer)라고 한다.
서버 기반 모델 | P2P 모델 |
- 안정적인 서비스의 제공이 가능하다. - 공유 데이터의 관리와 보완이 용이하다. - 서버구축비용과 관리비용이 든다. |
- 서버 구축 및 운용비용을 절감할 수 있다. - 자원의 활용을 극대화할 수 있다. - 자원의 관리가 어렵다. - 보안이 취약하다. |
2. IP주소
(1) IP주소는 컴퓨터를 구분하는데 사용되는 고유한 값으로 인터넷에 연결된 모든 컴퓨터는 IP주소를 갖는다.
(2) IP주소는 4byte(32bit)의 정수로 구성되어 있으며, 1byte단위의 정수가 맟침표를 구분자로 표현된다.
(3) IP주소는 네트워크 주소와 호스트 주소로 나눌 수 있다. 네트워크의 구성에 따라 네트워크 주소와 호스트 주소가 각각 몇 bit를 차지하는지 달라진다. (서로 다른 두 호스트의 IP주소의 네트워크 주소가 같다는 것은 두 호스트가 같은 네트워크에 포함되어 있다는 것을 의미한다.)
(4) IP 주소에서 네트워크 주소가 차지하는 자리수가 많을 수록 호스트 주소가 줄어들고 네트워크의 규모가 작아진다.
(5) 호스트 주소가 0인 것은 네트워크 자신을 나타내고, 255는 브로드캐스트 주소로 사용된다. 따라서 실제 네트워크에 포함 가능한 호스트의 수는 254개이다.
(6) IP주소와 서브넷 마스크를 &연산하면 네트워크 주소를 얻어낼 수 있다. 두 호스트가 같은 네트워크 상에 존재하는지 쉽게 확인할 수 있다.
(7) 자바에서는 IP주소를 다루기 위한 클래스로 InetAddress를 제공한다.
링크 : InetAddress (Java Platform SE 8 ) (oracle.com)
3. URL
(1) URL은 인터넷에 존재하는 여러 서버들이 제공하는 자원에 접근할 수 있는 주소를 표현하기 위한 것이다.
프로토콜://호스트명:포트번호/경로명/파일명?쿼리스트링#참조
- 프로토콜 : 자원에 접근하기 위해 서버와 통신하는데 사용되는 통신규약(http, https)
- 호스트명 : 자원을 제공하는 서버의 이름(docs.oracle.com)
- 포트번호 : 통신에 사용되는 서버의 포트번호(80)
- 경로명 : 접근하려는 자원이 저장된 서버상의 위치(/javase/8/docs/api/java/net/)
- 파일명 : 접근하려는 자원의 이름 (InetAddress.html)
- 쿼리 : URL에서 ?이후의 부분(type=post)
- 참조 : URL에서 #이후의 부분(index1)
(2) HTTP 프로토콜에서는 80번 포트를 사용한다. URL에서 포트번호를 생략하는 경우 80으로 간주한다.
(3) 각 프로토콜에 따라 통신에 사용하는 포트번호가 다르며 생략되면 각 프로토콜의 기본 포트가 사용된다.
(4) 자바에서는 URL 다루기 위해 URL 클래스를 제공한다.
링크 : URL (Java Platform SE 8 ) (oracle.com)
(5) URLConnection은 어플리케이션과 URL간의 통신연결을 나타내는 클래스의 최상위 클래스로 추상클래스이다. HttpURLConnection과 JarURLConnection은 URLConnection을 상속받아 구현한 클래스로 URL의 프로토콜이 http 프로토콜이라면 openConnection( )은 HttpURLconnection을 반환한다. URLConnection을 사용해서 연결하고자하는 자원에 접근하고 읽고 쓰기를 할 수 있다.
링크 : URLConnection (Java Platform SE 8 ) (oracle.com)
4. 소켓 프로그래밍
(1) 소켓이란 프로세스간의 통신에 사용되는 양쪽 끝단을 의미한다. 프로세스간 통신을 위해서는 소켓이 필요하다.
TCP | UDP | |
연결방식 | 연결기반 - 연결 후 통신 - 1:1 통신방식 |
비연결기반 - 연결 없이 통신 - 1:1, 1:n, n:n 통신방식 |
특징 | 데이터의 경계를 구분하지 않는다.(byte-stream) 신뢰성 있는 데이터 전송 - 데이터의 전송 순서가 보장됨 - 데이터의 수신여부를 확인 - 패킷을 관리할 필요가 없음 UDP보다 전송속도가 느림 |
데이터의 경계를 구분함(datagram) 신뢰성 없는 데이터 전송 - 데이터의 전송순서가 바뀔 수 있음 - 데이터의 수신여부를 확인안함 - 데이터가 손실되어도 알 수 없음 - 패킷을 관리해주어야함 TCP보다 전송속도가 빠름 |
관련 클래스 | Socket ServerSocket |
DatagramSocket DatagramPacket MulticastSocket |
(2) TCP는 데이터를 전송하기 전에 먼저 상대편과 연결을 한 후 데이터를 전송하며 잘 전송되었는지 확인하고 실패했다면 데이터를 재전송한다. 신뢰 있는 데이터의 전송이 요구되는 통신에 적합하다. (ex - 파일전송)
(3) UDP는 상대편과 연결하지 않고 데이터를 전송한다. 데이터를 전송하지만 데이터가 바르게 수신되었는지 확인하지 않기 때문에 데이터가 전송되었는지 확인할 길이 없다. (데이터를 보낸 순서대로 수신한다는 보장x)
게임이나 동영상 같이 데이터가 중간에 손실되더라도 빠른 전송이 필요할 때 적합하다.
5. TCP 소켓 프로그래밍
(1) TCP 소켓 프로그래밍은 먼저 서버 프로그램이 실행되어 클라이언트 프로그램의 연결요청을 기다리고 있어야한다.
(2) 서버 소켓은 포트와 결합되어 포트를 통해 원격 사용자의 연결요청을 기다리다가 새로운 소켓을 생성하여 상대편 소켓과 통신할 수 있도록 연결하는 역할을 한다. 실제적인 데이터의 통신은 서버소켓과 관계없이 소켓간에 이루어진다.
(3) 소켓들이 데이터를 주고받는 연결통로는 입출력스트림이다. 소켓은 입력스트림과 출력스트림을 가지고 있으며 상대편 소켓의 스트림들과 교차연결된다.
(4) Socket클래스는 프로세스간 통신을 담당한다. InputStream과 OutputStream을 가지고 있으며 두 스트림을 통해 프로세스간의 통신이 이루어진다.
(5) ServerSocket클래스는 포트와 연결되어 외부의 연결요청을 기다리다 연결요청이 들어오면 Socket을 생성해서 소켓간 통신이 이루어지도록 한다. 한 포트에 하나의 ServerSocket만 연결할 수 있다.
(6) ServerSocket클래스의 setSoTimeOut(int timeout)을 사용해서 서버소켓의 대기시간을 지정할 수 있다. timeout 값이 0일경우 제한시간 없이 대기한다. 지정한 대기시간이 지나면 accept( )에서 SocketTimeoutException이 발생한다.
(7) 서버에 접속하는 클라이언트의 수가 많을 때는 쓰레드를 이용해서 클라이언트의 요청을 병렬적으로 처리하는 것이 좋다. (서버가 접속을 요청한 순서대로 처리하면 늦게 접속을 요청한 클라이언트는 오랜 시간을 기다릴 수 밖에 없기 때문)
6. UDP 소켓 프로그래밍
(1) UDP 소켓 프로그래밍에서는 DatagramSocket과 DatagramPacket을 사용한다.
(2) UDP는 ServerSocket을 필요로 하지 않는다. UDP통신에서 사용하는 소켓은 DatagramSocket이며 데이터를 DatagramPacket에 담아서 전송한다.
(3) DatagramPacket은 헤더와 데이터로 구성되어 잇으며 헤더에는 DatagramPacket을 수신할 호스트의 정보(호스트의 주소, 포트)가 저장되어 있다. DatagramPacket을 전송하면 지정된 주소의 DatagramSocket에 도착한다.
다음은 수업 중 개인 Project에 적용한 내용을 정리하였습니다.
네트워킹을 통한 데이터 공유 단계별 정리)
System Architecture)
아래와 같이 App 클래스를 Client App 과 Server App 으로 분리합니다.
Socket 연결)
IP 와 Port 번호)
Project ServerApp)
package bitcamp.myapp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import com.google.gson.Gson;
import bitcamp.myapp.dao.BoardDao;
import bitcamp.myapp.dao.BoardListDao;
import bitcamp.myapp.dao.MemberDao;
import bitcamp.myapp.dao.MemberListDao;
public class ServerApp {
int port;
ServerSocket serverSocket;
MemberDao memberDao = new MemberListDao("member.json");
BoardDao boardDao = new BoardListDao("board.json");
BoardDao readingDao = new BoardListDao("reading.json");
public ServerApp(int port) throws Exception {
this.port = port;
}
public void close() throws Exception {
serverSocket.close();
}
public static void main(String[] args) throws Exception {
if (args.length < 1) {
System.out.println("실행 예) java ... bitcamp.myapp.ServerApp 포트번호");
return;
}
ServerApp app = new ServerApp(Integer.parseInt(args[0]));
app.execute();
app.close();
}
public void execute() throws Exception {
System.out.println("[MyLsit 서버 애플리케이션]");
this.serverSocket = new ServerSocket(port);
System.out.println("서버 실행 중...");
Socket socket = serverSocket.accept();
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
Gson gson = new Gson();
while (true) {
String command = in.readUTF();
System.out.println(command);
HashMap<String, String> response = new HashMap<>();
if (command.equals("quit")) {
break;
} else if (command.equals("board/list")) {
response.put("status", "success");
response.put("data", gson.toJson(boardDao.list()));
} else {
response.put("status", "failure");
response.put("message", "nono!");
}
out.writeUTF(gson.toJson(response));
}
in.close();
out.close();
socket.close();
}
}
Project ClientApp)
package bitcamp.myapp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import bitcamp.myapp.dao.BoardDao;
import bitcamp.myapp.dao.BoardNetworkDao;
import bitcamp.myapp.dao.MemberDao;
import bitcamp.myapp.dao.MemberNetworkDao;
import bitcamp.myapp.handler.BoardAddListener;
import bitcamp.myapp.handler.BoardDeleteListener;
import bitcamp.myapp.handler.BoardDetailListener;
import bitcamp.myapp.handler.BoardListListener;
import bitcamp.myapp.handler.BoardUpdateListener;
import bitcamp.myapp.handler.FooterListener;
import bitcamp.myapp.handler.HeaderListener;
import bitcamp.myapp.handler.HelloListener;
import bitcamp.myapp.handler.MemberAddListener;
import bitcamp.myapp.handler.MemberDeleteListener;
import bitcamp.myapp.handler.MemberDetailListener;
import bitcamp.myapp.handler.MemberListListener;
import bitcamp.myapp.handler.MemberUpdateListener;
import bitcamp.util.BreadcrumbPrompt;
import bitcamp.util.Menu;
import bitcamp.util.MenuGroup;
public class ClientApp {
Socket socket;
DataOutputStream out;
DataInputStream in;
MemberDao memberDao;
BoardDao boardDao;
BoardDao readingDao;
BreadcrumbPrompt prompt = new BreadcrumbPrompt();
MenuGroup mainMenu = new MenuGroup("메인");
public ClientApp(String ip, int port) throws Exception {
this.socket = new Socket(ip, port);
this.out = new DataOutputStream(socket.getOutputStream());
this.in = new DataInputStream(socket.getInputStream());
this.memberDao = new MemberNetworkDao("member", in, out);
this.boardDao = new BoardNetworkDao("board", in, out);
this.readingDao = new BoardNetworkDao("reading", in, out);
prepareMenu();
}
public void close() throws Exception {
prompt.close();
out.close();
in.close();
socket.close();
}
public static void main(String[] args) throws Exception {
if (args.length < 2) {
System.out.println("실행 예) java ... bitcamp.myapp.ClientApp 서버주소 포트번호");
return;
}
ClientApp app = new ClientApp(args[0], Integer.parseInt(args[1]));
app.execute();
app.close();
}
static void printTitle() {
System.out.println("나의 목록 관리 시스템");
System.out.println("----------------------------------");
}
public void execute() {
printTitle();
mainMenu.execute(prompt);
try {
out.writeUTF("quit");
} catch (Exception e) {
System.out.println("종료 오류!!");
e.printStackTrace();
}
}
private void prepareMenu() {
MenuGroup memberMenu = new MenuGroup("회원");
memberMenu.add(new Menu("등록", new MemberAddListener(memberDao)));
memberMenu.add(new Menu("목록", new MemberListListener(memberDao)));
memberMenu.add(new Menu("조회", new MemberDetailListener(memberDao)));
memberMenu.add(new Menu("변경", new MemberUpdateListener(memberDao)));
memberMenu.add(new Menu("삭제", new MemberDeleteListener(memberDao)));
mainMenu.add(memberMenu);
MenuGroup boardMenu = new MenuGroup("게시글");
boardMenu.add(new Menu("등록", new BoardAddListener(boardDao)));
boardMenu.add(new Menu("목록", new BoardListListener(boardDao)));
boardMenu.add(new Menu("조회", new BoardDetailListener(boardDao)));
boardMenu.add(new Menu("변경", new BoardUpdateListener(boardDao)));
boardMenu.add(new Menu("삭제", new BoardDeleteListener(boardDao)));
mainMenu.add(boardMenu);
MenuGroup readingMenu = new MenuGroup("독서록");
readingMenu.add(new Menu("등록", new BoardAddListener(readingDao)));
readingMenu.add(new Menu("목록", new BoardListListener(readingDao)));
readingMenu.add(new Menu("조회", new BoardDetailListener(readingDao)));
readingMenu.add(new Menu("변경", new BoardUpdateListener(readingDao)));
readingMenu.add(new Menu("삭제", new BoardDeleteListener(readingDao)));
mainMenu.add(readingMenu);
Menu helloMenu = new Menu("안녕!");
helloMenu.addActionListener(new HeaderListener());
helloMenu.addActionListener(new HelloListener());
helloMenu.addActionListener(new FooterListener());
mainMenu.add(helloMenu);
}
}
'[Naver Cloud Camp 7] 교육 정리' 카테고리의 다른 글
네이버 클라우드 캠프 53일차 230710 (0) | 2023.07.11 |
---|---|
네이버 클라우드 캠프 51일차 230706 (0) | 2023.07.06 |
네이버 클라우드 캠프 49일차 230704 (0) | 2023.07.05 |
네이버 클라우드 캠프 48일차 230703 (1) | 2023.07.04 |
네이버 클라우드 캠프 47일차 230630 (0) | 2023.06.30 |