※ 이 글에서 다룬 기반기술, 프론트엔드, 백엔드, 데브옵스 등 풀스택 개발 지식은 모두 한화시스템 Beyond SW Camp 12기에서 배운 내용을 복습한 것입니다.
스트림
자바에서 데이터는 스트림을 통해 입출력된다. 스트림은 단일 방향으로 연속적으로 흘러가는 것을 말한다, 프로그램이 출발지냐 도착지냐에 따라 사용하는 스트림의 종류가 결정된다.
프로그램이 도착지면 흘러온 데이터를 입력받아야 하므로 입력 스트림, 프로그램이 출발지면 데이터를 출력해야하므로 출력 스트림을 사용해야한다.
입출력 스트림 종류
- 바이트 기반 스트림 : 그림, 멀티미디어 등 바이너리 데이터 읽고 출력할 때 사용
- 문자 기반 스트림 : 문자 데이터를 읽고 출력할 때 사용
구분 | 바이트 기반 스트림 | 문자 기반 스트림 | ||
최상위 클래스 | InputStream | OutputStream | Reader | Writer |
하위 클래스 | xxxInputStream (FileInputStream) |
xxxOutputStream (FileOutputStream) |
XXXReader (FileReader) |
XXXWriter (FileWriter) |
바이트 기반 입력 스트림 예시
public class Main {
public static void main(String[] args) {
FileInputStream fis;
try {
// 스트림을 여는 것
fis = new FileInputStream(("c:\\test\\abc.txt"));
int i = 0;
// 한 바이트씩 끝까지 읽기
while ((i = fis.read()) != -1) {
// 정수형으로 반환된 값을 문자로 변환하여 출력
System.out.println((char) i);
}
// 스트림을 닫는 것
fis.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
이 코드를 간단히 설명하자면 c드라이브 밑에 test 폴더 밑에 있는 abc.txt라는 텍스트 파일의 데이터를 입력 스트림을 이용하여 읽어와서 모니터에 출력한다.
파일에서 한 바이트씩 읽어서 모니터에 출력한다. fis.read()는 한 바이트씩 정수로 반환하기 때문에 출력할 때 문자로 변환을 해줘야한다.
바이트 기반 출력 스트림 예시
public class Main {
public static void main(String[] args) {
// 파일의 내용을 저장하는 코드
FileOutputStream fos;
String data = "zxcv";
// "zxcv" 문자열을 배열로 변환
char [] chars = data.toCharArray();
try {
fos = new FileOutputStream("c:\\test\\abc2.txt"); //저장할 파일 경로 입력
//파일에 1바이트를 저장하는 코드
fos.write(chars[0]);
fos.write(chars[1]);
fos.write(chars[2]);
fos.write(chars[3]);
fos.close();
} catch (FileNotFoundException e) {
throw new RuntimeException();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
이 코드는 c 드라이브의 test 폴더에 있는 abc2.txt라는 텍스트 파일에 출력 스트림을 이용하여 "zxcv"라는 문자를 저장하는 코드이다.
입력 스트림, 출력 스트림 모두 작업이 끝난 후 .close() 메서드를 써줬는데 스트림을 꼭 닫아줘야 리소스가 해제되고 데이터가 제대로 저장된다.
문자 기반 입출력 스트림 예시
public class Cp {
public static void main(String[] args) {
System.out.println(args[0]); // 첫번째 매개변수를 출력
System.out.println(args[1]); // 두번째 매개변수를 출력
BufferedReader reader = null;
BufferedWriter writer = null;
try {
reader = new BufferedReader(new FileReader(args[0])); // 입력 파일 경로
writer = new BufferedWriter(new FileWriter(args[1])); // 출력 파일 경로
String line;
while ((line = reader.readLine()) != null) {
// 파일에서 한 줄씩 읽어서 화면에 출력
System.out.print(line);
writer.write(line);
writer.newLine();
}
reader.close();
writer.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
위의 코드는 문자 기반 입출력 스트림을 이용해서 입력 파일의 내용을 읽어 출력 파일로 복사하는 작업을 한다.
입출력 스트림을 이용한 네트워크 통신
public class Server {
public static void main(String[] args) {
ServerSocket serverSocket;
try{
serverSocket = new ServerSocket(1234);
System.out.println("클라이언트 접속 대기 중");
Socket socket = serverSocket.accept();
System.out.println("클라이언트 접속 성공");
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String filePath = in.readLine();
System.out.println("요청 받은 파일의 경로 " + filePath);
// 파일 읽어오기
//데이터를 보내는 코드
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("Server Hello");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public class Client {
public static void main(String[] args) {
Socket socket;
try{
socket = new Socket("127.0.0.1", 1234);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("c:\\test\\test.txt");
//데이터를 받는 코드
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String result = in.readLine();
System.out.println(result);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Server와 Client의 동작을 살펴보겠다.
Server 측
1. 1234포트로 클라이언트가 접속하길 기다리는 ServerSocket 생성
2. serverSocket.accept() 메서드는 클라이언트가 연결될 때 까지 대기하다가 접속이 되면 Socket 객체를 생성한다.
3. 클라이언트로부터 데이터를 읽기 위해 입력 스트림을 생성
4. 클라이언트가 보내는 파일 경로 한 줄 씩 읽기
5. 서버가 클라이언트에게 데이터를 보내기 위한 출력 스트림 생성
6. 서버가 클라이언트에게 "Server Hello" 메세지 전송
Client 측
1. 클라이언트가 127.0.0.1의 1234포트로 연결 시도
2. 서버로 데이터를 보내기 위해 출력 스트림 생성
3. 서버에 파일 경로 전송
4. 서버의 응답을 받기 위한 입력 스트림 생성
5. 서버로 부터 받은 응답("Server Hello") 읽은 후 출력
URL로 HTTP GET 요청을 보낸 후 응답을 출력
public class MyWebBrowser {
public static void main(String[] args) {
try {
// ex)192.168.113.10/a/b/test.html
// 입력받는 코드
String input;
Scanner sc = new Scanner(System.in);
input = sc.nextLine();
String serverAddr = input.split("/")[0];
String reqPath = "";
for (int i = 1; i < input.split("/").length; i++) {
reqPath += "/";
reqPath += input.split("/")[i];
}
Socket socket = new Socket(serverAddr, 80); // 서버와 연결 정보를 저장하고 있는 객체
// 데이터를 보내는 코드
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("GET " + reqPath + " HTTP/1.1");
out.println("Host: " + serverAddr);
out.println("Content-Type: text/html");
out.println();
// 데이터를 받는 코드
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
String line = in.readLine();
if (line == null) break;
if (line.contains(".css")) {
System.out.println("CSS 파일 받아오기");
}
System.out.println(line);
}
out.close();
in.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
위 코드는 URL로 HTTP GET 요청을 보낸 후 응답을 출력하는 코드이다. Scanner 객체로 192.168.113.10/a/b/test.html와 같은 내용을 입력받게 된다. spilt() 메서드로 /를 기준으로 분리하게 되는데 분리했을 경우 맨 앞에 있는 192.168.113.10이 serverAddr에, /a/b/test.html이 reqPath에 저장되게 된다. 그러면 Socket 객체를 이용해서 serverAddr와 포트번호(80번 HTTP)에 연결한다. 그 후 PrintWriter 객체를 이용해 HTTP GET 요청을 보내고 서버로 부터 응답을 받아 출력 코드이다.
'한화시스템 Beyond SW Camp > 백엔드' 카테고리의 다른 글
[Java] MVC 패턴, Layered 패턴 (0) | 2025.01.15 |
---|---|
[IntelliJ] Tomcat Servlet, mariaDB 연동 (0) | 2025.01.08 |
[Java] 스레드 (1) | 2025.01.08 |
[Java] 생성자, 상속, 예외처리 (1) | 2025.01.06 |
[Java] 변수 선언, 배열, 클래스와 객체, 패키지와 접근제어자 (0) | 2025.01.06 |