본문 바로가기
네이버클라우드/JAVA 웹 프로그래밍

JAVA 32일차 (2023-07-06) 자바 프로그래밍_네트워킹을 이용하여 데이터 공유하기 : Client/Server 아키텍처로 전환(계속)_Client와 Server 개념/프로토콜에 따라 애플리케이션 간에 데이터를 주고 받기_개..

by prometedor 2023. 7. 6.
## 37. 네트워킹을 이용하여 데이터 공유하기 : Client/Server 아키텍처로 전환(이어서 계속)

- 네트워크 프로그래밍 방법
  - Client와 Server 개념
  - 프로토콜에 따라 애플리케이션 간에 데이터를 주고 받기

 

 

BoardNetworkDao.java

ㄴ 서버에서 보낼 명령과 데이터를 Map 객체에 담기

 

BoardNetworkDao.java

ㄴ 주어진 코드에서 request 객체의 "data" 키에 board 객체를 JSON 형식으로 변환한 값을 설정

=> 이를 위해 Gson 라이브러리를 사용하여 board 객체를 JSON 문자열로 변환한 후, 해당 JSON 문자열을 "data" 키에 설정

 

BoardNetworkDao.java

ㄴ Map 객체에 담은 정보를 JSON 문자열로 변환하여 서버에 보내기

 

BoardNetworkDao.java

ㄴ Gson 객체를 맨 위에서 한 번만 생성하여 이용

 

BoardNetworkDao.java

ㄴ 해당 코드 제거 

 

BoardNetworkDao.java

ㄴ 주석으로 잠시 막아두고 해당 코드 추가

 

BoardNetworkDao.java

ㄴ GsonBuilder 의 setPrettyPrinting 메서드를 이용해서 gson.toJson(request) 값 정갈하게 출력해보기

=>

ㄴ JSON 형식의 값 확인 가능

 

BoardNetworkDao.java

ㄴ 다시 GsonBuilder => Gson 으로 변경

 

BoardNetworkDao.java

ㄴ 주석 다시 풀어주기

 

BoardNetworkDao.java

ㄴ message => result 로 변경해주기

 

BoardNetworkDao.java

ㄴ board 를 JSON 으로 바꾸는 이유는 꺼낼 때 쉽게 꺼내기 위해서임

 

BoardNetworkDao.java

ㄴ list 메서드에서는 insert 메서드에서 코드 복사 후 insert => list 로 변경

ㄴ insert 메서드와 동일하게 RuntimeException 에는 message 대신 result 를 넘겨주도록 함

 

BoardNetworkDao.java

ㄴ execute() 메서드에서 "quit" 명령을 서버로 전송하여 클라이언트 애플리케이션을 종료하도록 작성
ㄴ "quit" 명령을 서버에 전송하기 위해 HashMap인 request를 생성

     => "command" 키에는 "quit" 값을 설정
ㄴ out.writeUTF(new Gson().toJson(request))를 사용하여 request 맵을 JSON 형식의 문자열로 변환하고, 변환된 문자열을 DataOutputStream을 통해 서버로 전송
ㄴ 전송 중 발생하는 예외는 catch 블록에서 처리되며, "종료 오류!" 메시지를 출력하고 예외의 스택 트레이스를 출력
ㄴ 서버에 "quit" 명령을 전송함으로써 클라이언트 애플리케이션이 종료됨

 

BoardNetworkDao.java

ㄴ while 루프를 사용하여 계속해서 서버에서 전송된 JSON 문자열을 읽어옴

ㄴ in.readUTF()를 사용하여 DataInputStream을 통해 JSON 문자열을 읽어옴
ㄴ 읽어온 JSON 문자열은 gson.fromJson()을 사용하여 Map으로 변환

    => 변환할 클래스 타입으로 Map.class를 전달
ㄴ 변환된 Map에서 "command" 키를 사용하여 실제 명령을 추출 

 

=> 서버로부터 전송된 JSON 문자열을 읽어와서 명령을 추출

 

BoardNetworkDao.java

ㄴ 오류 무시를 위해 @SuppressWarnings("unchecked") 추가

 

BoardNetworkDao.java

ㄴ 아까 위에서 result 로 넘겨주기로 했으므로 키 값을 result 로 변경

ㄴ data => result 로 변경

ㄴ message => result 로 변경

 

ㄴ report-client 의 bitcamp 패키지에 net 이라는 이름으로 패키지 생성

 

ㄴ bitcamp.net 패키지에 RequestEntity 클래스 생성

 

RequestEntity.java

ㄴ command, data 인스턴스 변수 생성

 

메서드 체이닝

RequestEntity.java

ㄴ RequestEntity 클래스의 command() 메서드 생성

ㄴ 해당 메서드는 command 필드 값을 설정하고, RequestEntity 객체 자체를 반환함

    => 메서드 체이닝(메서드 호출을 연결하여 연속적으로 호출할 수 있는 구조)을 지원
=> RequestEntity 객체를 생성하고 command() 메서드를 체인으로 호출하여 command 값을 설정할 수 있음

 

RequestEntity.java

ㄴ RequestEntity 클래스의 data() 메서드 생성

ㄴ 해당 메서드는 data 필드 값을 설정하고, RequestEntity 객체 자체를 반환함

ㄴ 메서드 내부에서는 주어진 data 객체를 JSON 형식으로 변환하여 data 필드에 저장함(Gson 라이브러리 사용)
=> RequestEntity 객체를 생성하고 data() 메서드를 체인으로 호출하여 data 값을 설정할 수 있음

 

RequestEntity.java

ㄴ getter 만 생성

 

ClientApp.java

ㄴ 해당 코드 제거 후 아래처럼 변경 가능

=>

ClientApp.java

ㄴ RequestEntity 에 생성한 command 메서드 이용

=>

ClientApp.java

ㄴ 한 줄로 변경 가능 => 체이닝 기법

=>

ClientApp.java

ㄴ request 변수 생성할 필요 없도록 코드 변경 

 

BoardNetworkDao.java

ㄴ HashMap 대신 RequestEntity 에 생성한 command, data 메서드 이용

=>

BoardNetworkDao.java

ㄴ request 변수 생성하지 않아도 되도록 코드 변경

 

RequestEntity.java

ㄴ JSON 으로 바꾸는 코드 추가

 

BoardNetworkDao.java

ㄴ 체이닝 기법을 이용해 RequestEntity 클래스의 toJson 메서드를 이용함

 

ㄴ bitcamp.net 패키지에 ResponseEntity 클래스 생성

 

ResponseEntity.java

ㄴ status, result 인스턴스 변수 생성

 

ResponseEntity.java

ㄴ RequestEntity 클래스의 status 메서드 생성

ㄴ 해당 메서드는 status 필드 값을 설정하고, RequestEntity 객체 자체를 반환함

    => 메서드 체이닝(메서드 호출을 연결하여 연속적으로 호출할 수 있는 구조)을 지원
=> RequestEntity 객체를 생성하고 status 메서드를 체인으로 호출하여 status 값을 설정할 수 있음

 

ResponseEntity.java

ㄴ ResponseEntity 클래스의 result() 메서드

ㄴ 해당 메서드는 result 필드 값을 설정하고, ResponseEntity 객체 자체를 반환

ㄴ 메서드 내부에서는 주어진 obj 객체를 JSON 형식으로 변환하여 result 필드에 저장(Gson 라이브러리 사용)

 

ResponseEntity.java

ㄴ 만약 obj 객체의 클래스가 String 클래스와 동일하다면, obj를 그대로 result 필드에 저장하도록 코드 작성

 

RequesetEntity.java

ㄴ RequestEntity 클래스에서도 ResponseEntity 클래스처럼 조건문 추가해주기

=> RequsetEntity 클래스에서는 객체를 JSON 형식으로 변환하여 전송해야 함

 

ResponeEntity.java

ㄴ getter 만 생성

 

ResponeEntity.java

ㄴ fromJson 메서드를 생성하여 JSON 문자열을 ResponseEntity 객체로 변환하여 반환하는 역직렬화(deserialization) 작업을 수행하는 코드 작성

     => Gson 라이브러리의 fromJson 메서드는 JSON 문자열을 자바 객체로 변환하는 기능을 제공
ㄴ fromJson 메서드는 주어진 JSON 문자열 json을 ResponseEntity 클래스의 객체로 변환하여 반환

     => 클라이언트나 서버에서 전송된 JSON 응답을 다시 ResponseEntity 객체로 변환하여 필요한 데이터를 추출하고 처리

 

BoardNetworkDao.java

ㄴ 이제 Gson 객체는 필요 없으므로 제거

ㄴ ResponseEntity 객체로 만들어서 response 변수에 저장하도록 함

ㄴ Map 사용 시 이용했던 get("status") => getStatus() 로 변경 (getter 이용)

ㄴ Map 사용 시 이용했던 get("result") => getResult() 로 변경 (getter 이용)

 

ResponeEntity.java

ㄴ 개발자가 오타를 낼 수도 있으므로 상수로 정의해주기

 

BoardNetworkDao.java

ㄴ ResponseEntity 에 정의한 FAILURE 상수 값 이용

ㄴ !response ... ("success") => response ... (ResponseEntity.FAILURE) 로 변경

 

BoardNetworkDao.java

ㄴ 해당 코드 필요 없으므로 제거

 

BoardNetworkDao..java

ㄴ Gson 필요 없으므로 제거

ㄴ Map 대신 RequestEntity 클래스 이용

 

BoardNetworkDao..java

ㄴ Map 대신 ResponseEntity 클래스 이용

ㄴ ResponseEntity 에 정의한 FAILURE 상수 값 이용

ㄴ !response ... ("success") => response ... (ResponseEntity.FAILURE) 로 변경

ㄴ getResult() 는 String 값이므로 형변환 필요 없음

 

BoardNetworkDao..java

ㄴ ResponseEntity 클래스에 getList 메서드 추가할 것임

=>

ResponseEntity.java

ㄴ 제네릭 이용하여 getList 메서드 추가

 

ResponseEntity.java

ㄴ result 필드에 저장된 JSON 문자열을 주어진 클래스(clazz)의 타입으로 변환하여 해당 타입의 리스트로 반환

    => Gson 라이브러리의 fromJson 메서드를 사용

ResponseEntity.java

ㄴ BoardNetworkDao 클래스에서 해당 코드 가져옴

=>

ResponseEntity.java

ㄴ TypeToken import 해주기

ㄴ Board => clazz 로 변경

 

BoardNetworkDao..java

ㄴ 해당 코드 필요 없으므로 제거

 

ClientApp.java

ㄴ result 변수 생성할 필요 없도록 toJson 메서드에 new RequestEntity().command("quit") 코드를 넣어줌

ㄴ new Gson().toJson() => RequestEntity 클래스의 toJson() 이용하기

 

실행 test

ㄴ 실행 잘 됨을 확인

 

ServerApp.java

ㄴ insert 추가해주기

 

ㄴ app-client 복사하여 app-common 생성하기

 

ㄴ settings.gradle 에서 app-common  추가

 

ㄴ application 필요 없으므로 삭제

ㄴ java-library 이용하기 위해 applicatioin 제거 후 'java-library' 플러그인 추가

     => java-library: Java 라이브러리 프로젝트를 구성하기 위한 기능을 제공

          ㄴ Java 라이브러리를 컴파일하고 패키징하는 데 필요한 태스크와 설정이 자동으로 추가됨

ㄴ build.gradle 스크립트 파일에서 프로젝트 이름 report-common 으로 수정

 

ㄴ gradle 재설정 해주기

 

ㄴ app-common 에 존재하는 ClientApp 삭제해주기

 

ㄴ 생성한 app-command 워크스페이스에 import 해주기

 

ㄴ app-client 를 복사한 프로젝트이므로 삭제할 파일들 삭제해주기

ㄴ app-client 에서도 net 패키지 삭제

 

report-client 프로젝트의 build.gradle

ㄴ 서브 프로젝트의 클래스를 사용하기 위해 해당 코드 추가

 

ㄴ gradle 재설정 해주기

 

=>

ㄴ 에러 없어짐

 

=> report-server 도 같은 방법으로 적용해주기

 

 

ServerApp.java

ㄴ Map 대신 myapp-common 프로젝트(서브 프로젝트)의 bitcamp.net 패키지에 있는 RequestEntity 사용

 

RequestEntity.java

ㄴ Gson 라이브러리를 사용하여 JSON 문자열을 RequestEntity 객체로 변환하는 fromJson 메서드 생성

 

ServerApp.java

ㄴ Map 이 아니므로 Map.class 제거

 

ServerApp.java

ㄴ Map 이 아닌 myapp-common 프로젝트(서브 프로젝트)의 bitcamp.net 패키지에 있는 RequestEntity 사용하므로 해당 클래스에 있는 getter 메서드인 getCommand() 이용

 

ServerApp.java

ㄴ Map 이 아닌 myapp-common 프로젝트(서브 프로젝트)의 bitcamp.net 패키지에 있는 RequestEntity 사용

 

ServerApp.java

ㄴ Map 이 아닌 myapp-common 프로젝트(서브 프로젝트)의 bitcamp.net 패키지에 있는 RequestEntity 사용

ㄴ status, result 메서드 이용

 

ServerApp.java

ㄴ Map 이 아닌 myapp-common 프로젝트(서브 프로젝트)의 bitcamp.net 패키지에 있는 RequestEntity 사용

ㄴ status, result 메서드 이용

 

ServerApp.java

ㄴ ResponseEntity 클래스에 toJson 메서드 추가해줄 것

 

ResponseEntity.java

ㄴ ResponseEntity 클래스에 toJson 메서드 추가함

 

ServerApp.java

ㄴ 해당 코드 필요 없으므로 제거

 

ServerApp.java

ㄴ insert 메서드 작성해주기

 

RequestEntity.java

ㄴ clazz가 String.class와 같다면, 데이터는 이미 문자열로 저장되어 있으므로 그대로 반환하도록 함

    => 이 경우 데이터는 T 타입으로 캐스팅하여 반환됨
ㄴ clazz가 String.class와 다르다면, Gson 라이브러리를 사용하여 데이터를 역직렬화하여 해당 클래스 타입으로 변환하여 반환함

    => 이 경우 Gson의 fromJson 메서드를 사용하여 JSON 문자열을 clazz의 인스턴스로 변환함

RequestEntity.java

ㄴ @SuppressWarnings("unchecked") 어노테이션은 경고를 억제하는 용도로 작성

 

RequestEntity.java

ㄴ ResponseEntity 클래스에 존재하는 getList 메서드 복사해오기

=>

RequestEntity.java

ㄴ result => data 로 변경

    => Request 에서는 data 로 되어있기 때문

 

ResponseEntity.java

ㄴ RequestEntity 클래스에 존재하는 getObject 메서드 복사해오기

=> 

RequestEntity.java

ㄴ data => result 로 변경

    => Response 에서는 result 로 되어있기 때문

 

ServerApp.java

ㄴ boardDao.insert(request.getObject(Board.class))는 boardDao를 사용하여 요청으로 전달된 Board 객체를 데이터베이스에 삽입하는 작업을 수행

ㄴ response.status(ResponseEntity.SUCCESS)는 응답 객체인 response의 상태를 "success"로 설정하는 메서드

    => 클라이언트에게 성공 상태를 전달함

=> Gson 객체가 필요 없음

 

Client

 

Server

 

BoardNetworkDao.java

ㄴ findBy 메서드는 list 메서드와 비슷하므로 list 메서드 복사해서 붙여넣은 후 수정하기

=>

BoardNetworkDao.java

ㄴ .data(no): data 메서드를 호출하여 요청의 데이터(data)를 설정함 (no는 조회할 게시글의 번호)

 

ServerApp.java

ㄴ request.getObject(Integer.class)는 요청의 데이터를 Integer 객체로 변환하여 반환하는 메서드임

    => 요청으로 전달된 게시글 번호를 가져올 수 있음
ㄴ boardDao.findBy(request.getObject(Integer.class))는 boardDao를 사용하여 요청으로 전달된 게시글 번호에 해당하는 게시글을 데이터베이스에서 조회하는 작업을 
ㄴ response.status(ResponseEntity.SUCCESS).result(board)는 응답 객체인 response의 상태를 "success"로 설정하고, 조회된 게시글을 응답 결과로 설정함

     => 클라이언트에게 성공 상태와 조회된 게시글을 전달함

 

ServerApp.java

ㄴ board 객체에 아무것도 담겨있지 않다면 응답 객체인 response의 상태를 "failure"로 설정하고, "해당 번호의 게시물이 없습니다!" 출력하도록 설정

ㄴ board 객체에 데이터가 담겨있다면 응답 객체인 response의 상태를 "success"로 설정하고, 조회된 게시글을 응답 결과로 설정함

 

BoardNetworkDao.java

ㄴ ResponseEntity 클래스에 있는 getObject(Board.class) 메서드를 이용하여 Board 객체로 변환하여 반환하도록 함

 

ResponseEntity.java

ㄴ error 를 상수 값으로 정의해두기

 

ServerApp.java

ㄴ "해당 명령을 지원하지 않습니다." 문장을 출력하는 것을 응답이 "error" 인 경우로 변경해줌

 

BoardNetworkDao.java

ㄴ FAILURE => ERROR 로 변경

ㄴ ResponseEntity.ERROR: 에러가 발생한 경우, 에러 메시지를 RuntimeException으로 던지도록 함
ㄴ ResponseEntity.FAILURE: 실패한 경우, null을 반환하도록 함

 

BoardNetworkDao.java

ㄴ ResponseEntity.ERROR인 경우에는 서버에서 발생한 에러 메시지를 RuntimeException으로 던지도록 함
ㄴ IOException이 발생한 경우에는 RuntimeException으로 래핑하여 던지도록 함

 

BoardNetworkDao.java

ㄴ ResponseEntity.FAILURE인 경우에는 서버에서 발생한 에러 메시지를 RuntimeException으로 던지도록 함
ㄴ IOException이 발생한 경우에는 RuntimeException으로 래핑하여 던지도록 함

 

BoardNetworkDao.java

ㄴ Exception => IOException 으로 변경

ㄴ IOException이 발생한 경우에는 RuntimeException으로 래핑하여 던지도록 함

 

실행 test

ㄴ 잘 실행됨을 확인

 

BoardNetworkDao.java

ㄴ update 는 insert 와 비슷하므로 해당 코드 insert 메서드에서 복사해오기

 

BoardNetworkDao.java

ㄴ 해당 코드는 findBy 메서드와 비슷하므로 findBy 메서드에서 복사해오기

ㄴ Board.class => Integer.class 로 변경

     => 리턴 값이 int 이기 때문

ㄴ response.getObject(Integer.class)를 호출하여 서버에서 받은 업데이트된 게시글의 번호를 반환함

     => Integer 는 자동 언박싱 되어 int 로 변환됨

          (Java에서는 기본 타입(primitive type)을 클래스로 표현하기 위해 래퍼 클래스를 사용함)

     => response.getObject(Integer.class) 에서 .intValue() 가 생략된 것 (컴파일 시 컴파일러가 자동으로 삽입함)

    ㄴ response.getObject(Integer.class)는 ResponseEntity 객체에서 Integer 형식의 데이터를 가져옴

        => getObject() 메서드는 Object 타입의 데이터를 반환하므로, Integer 형식으로 언박싱이 필요
    ㄴ response.getObject(Integer.class)가 Integer 객체를 반환할 경우 자동으로 언박싱되어 int 값으로 변환
        ㄴ ex) response.getObject(Integer.class)가 10을 반환한다면, 자동으로 언박싱되어 int 값인 10으로 사용 가능

 

ServerApp.java

ㄴ if 문을 switch 문으로 변경

ㄴ request.getObject(Board.class)는 RequestEntity 객체에서 data 필드를 가져와서 해당 필드 값을 Board 클래스로 변환하도록 함

ㄴ getObject() 메서드는 요청받은 데이터를 주어진 클래스 타입으로 변환하여 반환도록 함

     => request.getObject(Board.class)는 Board 객체를 얻기 위해 data 필드의 값을 Board 클래스로 변환하는 것을 의미함

 

실행 test

 

BoardNetworkDao.java

ㄴ findBy 메서드에서 해당 코드 복사해와서 수정하기

ㄴ findby => delete

 

BoardNetworkDao.java

ㄴ update 메서드에서 해당 코드 복사해오기

 

ServerApp.java

ㄴ delete 추가

ㄴ request 객체의 data 필드에 저장된 정수 값을 가져와서 Integer 객체로 변환

 

실행 test

ㄴ 잘 실행됨을 확인 

 

=> 나머지 ItemNetworkDao 와 MemberNetworkDao 모두 BoardNetworkDao 와 동일하게 완성해보기

 

 

파일 정리

ㄴ report-common 의 bitcamp.report.vo 패키지를 생성하여 report-server 의 bitcamp.vo 패키지에 있는 파일들 모두 복사하여 붙여넣기

 

ㄴ report-server 와 report-client 에 있는 bitcamp.report.vo 패키지는 삭제하기

 

ㄴ report-common 의 bitcamp.report 패키지에 dao 패키지 추가

 

ㄴ report-server 의 bitcamp.report.dao 패키지에서 각각의 XxxDao 파일만 복사하기

 

ㄴ report-common 에서 생성한 dao 에 복사해둔 XxxDao 파일들 붙여넣기

 

ㄴ 이제 report-server 의 bitcamp.report.dao 패키지에서 각각의 XxxDao 파일은 삭제

 

ㄴ report-client 의 bitcamp.report.dao 패키지에서 각각의 XxxDao 파일은 삭제

 

ㄴ report-client 의 bitcamp.report.dao 패키지에서 각각의 XxxListDao 파일들도 삭제

 

ㄴ report-server 의 bitcamp.report.handler 패키지 통째로 삭제

 

ㄴ report-server 의 bitcamp.test 패키지 통째로 삭제

 

ㄴ report-server 의 bitcamp.util 패키지에서 JsonDataHelper, Prompt 제외하고 모두 삭제

 

ㄴ report-client 의 bitcamp.test 패키지 통째로 삭제

 

ㄴ report-client 의 bitcamp.util 패키지에서 JsonDataHelper.java 삭제

 

ㄴ report-client 에서 build.gradle 을 제외한 데이터 파일 모두 삭제

 

ㄴ report-server 에서 build.gradle 파일과 json 파일 제외한 나머지 데이터 파일 모두 삭제