📌 고정 게시글

📢 공지합니다

이 게시글은 메인 페이지에 항상 고정되어 표시됩니다.

최코딩의 개발

[2부] CS 과제 공부하기 본문

회사/갤럭시아머니트리

[2부] CS 과제 공부하기

seung_ho_choi.s 2026. 1. 22. 14:15
728x90

1. Byte Stream vs Character Stream

https://xxeol.tistory.com/45

1-1. 이론

Stream은 데이터가 이동하는 통로이다. 데이터는 FIFO형태로 전송된다.

Stream의 가장 큰 특징은 단방향으로만 흘러간다는 점인데, 하나의 스트림으로 입출력을 같이 처리할 수 없어 입출력을 위해서는 두 개의 스트림(InputStream/OutputStream)이 필요하다.

InputStream은 외부에서 데이터를 읽는(입력) 역할을 수행하며, OuputStream은 외부로 데이터를 출력하는 역할을 수행한다.

 

Stream은 처리하는 데이터의 유형에 따라 크게 두 가지 유형으로 나뉜다.

 

1-2.  Byte Stream

데이터를 8비트(1 byte) 단위로 주고받는 가장 기본적인 스트림이다.

이들은 주로 이진 데이터(이미지, 동영상, 파일 등)를 다룰 때 사용된다.

 

추상 클래스 InputStream, OutputStream를 상속하여 구현한다.

https://xxeol.tistory.com/45

1byte는 범위가 0~255로 알파벳의 대/소문자 아스키 코드 값은 모두 해당 범위에 속한다. 하지만 한글의 아스키 코드 값을 나타내려면 2byte가 필요하고, 따라서 ByteStream에서는 한글이 깨진다.

 

그래서 한글 File을 읽고 쓸 때는 Character Streams(Reader/Writer)를 활용해야한다.

 

1-3. Character Stream

데이터를 16비트(2 byte) 단위로 처리하며, 문자 인코딩을 자동으로 처리해 준다. 

텍스트 데이터(문자)를 처리하기 위해 설계되었습니다. 세계의 모든 언어(유니코드)를 안전하게 입출력할 수 있습니다.

 

추상 클래스 Reader, Writer를 상속하여 구현한다.

https://xxeol.tistory.com/45

 

1-4. 결론

  • 텍스트 파일을 읽거나 쓸 때: 무조건 Character Stream을 권장한다. (특히 BufferedReader를 쓰면 성능이 훨씬 좋아진다.)
  • 그 외 모든 파일(이미지, PDF 등): Byte Stream을 사용해야 데이터 손실이 없다.
  • 바이트를 문자로 변환하고 싶을 때: InputStreamReader나 OutputStreamWriter라는 "브릿지(Bridge) 스트림"을 사용하면 바이트 스트림을 문자 스트림처럼 다룰 수 있다.
  •  이때 브릿지 스트림은 브릿지 스트림(InputStreamReader, OutputStreamWriter)은 캐릭터 스트림 계열에 속하지만,
    바이트 스트림과 캐릭터 스트림을 연결하는 변환 전용 스트림이다.
  • BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 이 코드가 대표케이스 코딩테스트 공부할때 그냥 썼는데 정확히 공부하니깐 의문점들이 풀렸다.

 


 

2. String, StringBuffer, StringBuilder 차이 및 장단점

2-1. 개요

자바에서는 대표적으로 문자열을 다루는 자료형 클래스로 String, StringBuffer, StringBuilder 라는 3가지 자료형을 지원한다. 위 3가지 클래스 자료형은 모두 문자열을 다루는데 있어 공통적으로 사용되지만, 사용 목적에 따라 쓰임새가 많이 달라지게 된다.

이번 목차에는  String, StringBuffer, StringBuilder 클래스 차이점을 알아보고, 이 3가지 중 어느 상황에서 어느 자료형을 사용하는 것이 이상적이고 성능적으로는 어느것이 더 좋은지 총정리 해보는 시간을 가져보겠다.

2-2. String

기본적으로 자바에서는 String 객체의 값은 변경할 수 없다. 이는 한번 할당된 공간이 변하지 않는다고 해서 '불변(immutable)' 자료형 이라고 불리운다. 그래서 초기공간과 다른 값에 대한 연산에서 많은 시간과 자원을 사용하게 된다는 특징이 있다.

String result = "";
result += "hello";
result += " ";
result += "jump to java";
System.out.println(result); // hello jump to java
// → 심플하지만 연산 속도가 느리다는 단점이 있다

 

String 자료형만으로도 + 연산자나 concat() 메소드를 사용해 문자열을 이어 붙일 수 있다. 그러나 + 연산자를 이용해 String 인스턴스의 문자열을 결합할 경우, 기존 문자열이 수정되는 것이 아니라 내용이 합쳐진 새로운 String 인스턴스가 매번 생성된다.

 

이로 인해 문자열을 많이 결합할수록 사용되지 않는 문자열 객체가 계속해서 생성되고 버려지게 되며, 그 결과 불필요한 메모리 낭비가 발생할 뿐만 아니라 문자열 결합 성능 또한 급격히 저하되는 단점이 있다.

 

이는 마치 글을 한 줄씩 추가할 때마다 기존 종이에 이어서 쓰는 것이 아니라,
새 종이를 꺼내 기존 내용을 모두 다시 옮겨 적은 뒤 새로운 내용을 덧붙이는 것과 같다.
문장이 길어질수록 종이를 더 많이 쓰게 되고, 다시 옮겨 적는 시간도 점점 늘어나게 된다.

2-3. StringBuffer / StringBuilder

2-3-1. 그들의 이론

이러한 비효율성을 해결하기 위해 자바에서는 문자열 연산을 전용으로 처리할 수 있는 StringBuffer(또는 StringBuilder) 클래스를 제공한다. 이들 클래스는 내부에 버퍼(buffer)라는 독립적인 공간을 두어 기존 문자열을 복사하지 않고 같은 공간에 문자열을 직접 추가하는 방식으로 동작한다.

StringBuffer sb = new StringBuffer();  // StringBuffer 객체 sb 생성
sb.append("hello");
sb.append(" ");
sb.append("jump to java");
String result = sb.toString();
System.out.println(result); // hello jump to java
// → + 연산보다는 복잡해 보이지만 연산 속도가 빠르다는 장점이 있다

 

이는 문자열을 이어 붙일 때마다 새 종이를 꺼내 다시 쓰는 대신, 하나의 화이트보드에 내용을 지우지 않고 계속 이어서 작성하는 것과 같다.

 

이미 적힌 내용을 다시 옮겨 적을 필요가 없기 때문에 불필요한 공간 낭비가 발생하지 않으며, 작업 속도 또한 훨씬 빠르다.

이와 같은 방식 덕분에 StringBuffer와 StringBuilder는 문자열을 반복적으로 결합해야 하는 상황에서
메모리 사용을 줄이고 문자열 연산 성능을 크게 향상시킬 수 있다. 

StringBuffer sb = new StringBuffer(); // 기본 16 버퍼 크기로 생성

// sb.capacity() - StringBuffer 변수의 배열 용량의 크기 반환
System.out.println(sb.capacity()); // 16 
 
sb.append("1111111111111111111111111111111111111111"); // 40길이의 문자열을 append
System.out.println(sb.capacity()); // 40 (추가된 문자열 길이만큼 늘어남)

 

기본적으로 StringBuffer의 버퍼(데이터 공간) 크기의 기본값은 16개의 문자를 저장할 수 있는 크기이며, 생성자를 통해 그 크기를 별도로 설정할 수도 있다.

만일 문자열 연산중에 할당된 버퍼의 크기를 넘게 되면 자동으로 버퍼를 증강 시키니 걱정 안해도 된다. 다만, 효율이 떨어질 수 있으므로 버퍼의 크기는 넉넉하게 잡는 것이 좋다.

 

2-3-2. StringBuffer와 StringBuilder의 차이

StringBuffer와 StringBuilder 클래스는 둘 다 크기가 유연하게 변하는 가변적인 특성을 가지고 있으며, 제공하는 메서드와 사용 방법도 동일하다.


그렇다면 이 둘의 차이점은 무엇일까? 어렵게 생각할 필요 없이, 사실 차이는 딱 한 가지이다.
바로 멀티 스레드(Thread) 환경에서 안전(thread-safe)하냐 아니냐의 차이이다.

 

StringBuffer는 모든 메서드에 동기화(synchronized)가 적용되어 멀티 스레드 환경에서도 안전하지만,
이로 인해 불필요한 락(lock) 비용이 발생하여 성능이 상대적으로 떨어질 수 있다.
반면 StringBuilder는 동기화를 지원하지 않는 대신 이러한 비용이 없어 더 빠르게 동작한다.

 


 

 

3. Call by Value / Call by Reference / Reflection

3-1. 개요

메서드를 호출할 때 전달되는 값이 어떤 방식으로 전달되는지에 따라 프로그램의 동작 결과가 달라질 수 있다.
이를 설명하는 개념이 바로 Call by ValueCall by Reference이다. 특히 자바에서는 객체를 전달할 때 동작 방식이 직관적이지 않아 Call by Reference로 오해하는 경우가 많다.

 

이번 목차에서는 Call by Value와 Call by Reference의 개념을 정리하고,
자바에서는 어떤 호출 방식을 사용하는지 예제를 통해 살펴본다.
또한 런타임 시점에 클래스 정보를 동적으로 다루는 Reflection 개념까지 함께 정리한다.

3-2. Call by Value (값에 의한 호출)

Call by Value는 메서드 호출 시 값(value)을 복사하여 전달하는 방식이다. 메서드 내부에서 전달받은 값을 변경하더라도
원본 변수에는 아무런 영향을 미치지 않는다. 자바에서 기본 자료형(primitive type)은 모두 Call by Value 방식으로 전달된다.

public class Test {
    static void change(int x) {
        x = 10;
    }

    public static void main(String[] args) {
        int a = 5;
        change(a);
        System.out.println(a); // 5
    }
}

 

위 코드에서 change() 메서드는 a의 값을 복사한 x를 전달받는다.
따라서 메서드 내부에서 x의 값을 변경하더라도
원본 변수 a의 값은 변경되지 않는다.

3-3. Call by Reference (참조에 의한 호출)

Call by Reference는 메서드 호출 시 변수의 주소(참조)를 전달하는 방식이다.
메서드 내부에서 값을 변경하면 원본 변수의 값도 함께 변경된다.

이 방식은 C++과 같은 언어에서 지원된다. (Java XXX)

#include <iostream>
using namespace std;

void change(int& x) {
    x = 10;
}

int main() {
    int a = 5;
    change(a);
    cout << a << endl; // 10
    return 0;
}

 

위 코드에서 int& xa메모리 주소를 직접 참조한다.
따라서 함수 내부에서 값을 변경하면
main 함수a 값도 함께 변경된다.

3-4. Java는 Call by Value일까? Call by Reference일까?

결론부터 말하면 자바는 100% Call by Value만 지원한다.
다만 객체를 전달할 때 참조값(reference value)을 값으로 전달하기 때문에
Call by Reference처럼 보이는 경우가 발생한다.

class Box {
    int value;
}

public class Test {
    static void change(Box box) {
        box.value = 10;
    }

    public static void main(String[] args) {
        Box b = new Box();
        b.value = 5;
        change(b);
        System.out.println(b.value); // 10
    }
}

 

위 예제에서는 객체의 참조값이 복사되어 전달되므로
같은 객체를 가리키게 되고, 그 결과 객체 내부 값이 변경된다.

하지만 참조 자체를 변경하면 원본에는 영향이 없다.

static void change(Box box) {
    box = new Box();
    box.value = 10;
}

 

이 경우 box는 새로운 객체를 가리키게 되며
원본 객체 b는 변경되지 않는다.
이를 통해 자바가 Call by Value 방식임을 확인할 수 있다.

 


4. Java Compile option

4-1. 개요

https://balhae.tistory.com/276

 

자바의 실행 환경

1. JVM이란?JVM(Java Virtual Machine)은 이름 그대로 '자바를 실행하기 위한 가상 기계'입니다. Java의 가장 큰 특징 중 하나는 OS에 종속적이지 않다는 점인데, 이것이 가능한 이유가 바로 이 JVM 때문입니

balhae.tistory.com

자바의 실행 흐름은 위 블로그에 있다. 컴파일 옵션에 관해 정확히 파헤쳐 보자.

 

자바 컴파일러(javac)는 우리가 작성한 소스 코드(.java)를 JVM이 이해할 수 있는 중간 단계 언어인 바이트코드(Bytecode, .class)로 변환한다. 이 과정은 단순히 파일을 바꾸는 것이 아니라, 컴파일러에게 "어떤 재료를 참고할지", "어느 버전의 JVM에 맞출지" 등을 명령하는 과정이다.

4-2. 핵심 옵션: -classpath (-cp)

자바 컴파일의 성패는 필요한 재료(라이브러리)를 얼마나 잘 찾느냐에 달려 있다.

 

4-2-1. Classpath란 무엇인가?

우리가 요리(컴파일)를 하려고 레시피(소스 코드)를 펼쳤는데, 재료 리스트에 "마트에서 파는 특제 소스(외부 라이브러리)"가 적혀 있다고 가정하자. 요리사(컴파일러)가 주방을 아무리 뒤져봐도 이 소스가 없다면 요리는 중단된다. 이때 요리사에게 "그 소스는 옆 동네 창고(특정 경로)에 있어!"라고 위치를 알려주는 행위가 바로 Classpath 설정이다.

 

4-2-2. 왜 에러(package does not exist)가 발생하는가?

error: package org.springframework... does not exist
error: package ???.????.?????? does not exist

 

스프링 프로젝트처럼 외부 라이브러리에 의존하는 코드를 리눅스에서 직접 컴파일할 때 이 옵션을 빼먹으면, 컴파일러는 라이브러리 위치를 몰라 아래와 같은 에러를 내뱉는다. 재료가 어디 있는지 알려주지 않은 셈이다.

# "lib 폴더 안의 모든(*) 재료를 참고해서 요리해라"
javac -classpath "프로젝트경로/WEB-INF/lib/*" SomeFile.java

 

이때 위와 같이 -classpath 옵션을 사용하여 라이브러리가 모여 있는 lib 폴더의 경로를 알려주면 된다.

# 리눅스는 콜론(:), 윈도우는 세미콜론(;)으로 경로 연결
javac -classpath "프로젝트경로/lib/*:톰캣경로/lib/*" SomeFile.java

 

만약 프로젝트 내부 재료뿐만 아니라 톰캣(Tomcat) 엔진의 재료까지 필요하다면 구분자(:)를 사용해 이어준다.

4-3. javac 컴파일 옵션 상세 정리

나중에 개발하면서 필요할때 찾아보자.

https://internet-craft.tistory.com/24

옵션 설명 실생활 비유
-bootclasspath 자바 기본 런타임 클래스 위치 지정 요리의 기본인 '물과 불'을 어디서 끌어올지 정함
-classpath, -cp 필요한 외부 클래스/라이브러리 위치 지정 특제 소스가 있는 옆 동네 창고 위치 알려주기
-d 생성된 클래스 파일 저장 위치 지정 요리 완성본을 담을 접시(폴더) 정하기
-deprecation 사용 권장되지 않는 API 사용 시 경고 "그 재료는 유통기한 임박이니 주의해!"라고 경고함
-encoding 소스 코드 문자의 인코딩 방식 지정 레시피가 한국어인지 영어인지 명시하기
-endorseddirs 승인된 표준 경로 위치 재정의 정부 인증을 받은 정품 재료 경로 지정
-extdirs 확장 기능(Extensions) 위치 재정의 주방에 추가로 설치한 특수 도구함 위치
-g 모든 디버깅 정보 생성 요리 과정을 처음부터 끝까지 영상으로 촬영하기
-g:none 디버깅 정보를 생성하지 않음 기록 없이 요리만 빠르게 끝내기
-help 표준 옵션 개요 출력 요리 도구 사용 설명서 읽기
-J<option> JVM에 직접 옵션 전달 가스레인지 화력(메모리)을 직접 조절하기
-nowarn 경고 메시지 출력 안 함 요리사가 궁시렁대는 소리(경고) 무시하기
-source 소스 코드의 자바 버전 지정 구버전 레시피인지 신버전 레시피인지 명시
-sourcepath 원본 소스 파일 위치 지정 재료 손질 전 원물(소스)이 쌓여있는 창고 지정
-target 결과물이 실행될 JVM 버전 지정 이 요리가 뷔페용인지(고버전), 도시락용인지(저버전) 정함
-verbose 컴파일 수행 상세 메시지 출력 요리사가 지금 뭐 하는지 하나하나 말하면서 요리함
-version 컴파일러 버전 정보 출력 요리사의 자격증 등급 확인하기
-X 비표준 옵션 개요 출력 요리사의 숨겨진 비기(필살기) 목록 보기

 

 

 


 

5. Java Memory - Garbage Collection

5-1. 개요

https://balhae.tistory.com/276

 

자바의 실행 환경

1. JVM이란?JVM(Java Virtual Machine)은 이름 그대로 '자바를 실행하기 위한 가상 기계'입니다. Java의 가장 큰 특징 중 하나는 OS에 종속적이지 않다는 점인데, 이것이 가능한 이유가 바로 이 JVM 때문입니

balhae.tistory.com

javac와 동일하게 가비지 컬렉션도 위 링크를 보면 자세히 알 수 있다. 

 

6. Java Static

6-1. 개요

자바에서 static 키워드는 "메모리에 한 번만 올라가고, 모든 객체가 공유한다"는 의미를 갖는다. 보통 변수나 메서드 앞에 붙여서 사용하며, 객체(인스턴스)를 생성하지 않고도 클래스 이름으로 바로 접근할 수 있다는 것이 가장 큰 특징이다.

 

https://balhae.tistory.com/34

 

열 세번째, static에 관해

개요: 필자가 하도 헷갈린 드디어 그녀석에 대한 풀이를 해보겠다. https://vaert.tistory.com/101 static - 정적(static)은 고정된 의미를 가지고 있다. - static 키워드를 사용하면 static 변수와 static 메소드를

balhae.tistory.com

무려 3년전에 작성한 게시글..... 지금 보니깐 AI 도움없이 작성했는데 나름 선방하게 작성한거 같다.

6-2. static 변수(정적 변수)

클래스 수준에서 정의된 변수로, 해당 클래스로 생성된 모든 객체가 하나의 메모리 공간을 공유한다.

  • 실생활 비유: 아파트 단지의 '공용 놀이터'와 같다. 집(객체)은 여러 채지만, 놀이터는 하나뿐이며 모든 입주민이 함께 사용한다. 누군가 놀이터에 낙서를 하면 모든 입주민에게 똑같이 보인다.
  • 특징:
    • 인스턴스 생성 전, 클래스가 로딩될 때 메모리(Method Area)에 할당된다.
    • 프로그램이 종료될 때까지 메모리에 유지된다.
    • 공통으로 사용되는 상수나 카운팅 변수에 자주 쓰인다.

6-3. static 메서드(정적 메서드)

객체를 생성하지 않고도 클래스명.메서드명()으로 호출할 수 있는 메서드이다.

  • 용도: 유틸리티 성격의 함수를 만들 때 주로 사용한다. (예: Math.abs(), String.valueOf())
  • 주의사항: static 메서드 안에서는 인스턴스 변수(static이 안 붙은 변수)를 사용할 수 없다. 인스턴스 변수는 객체가 생성되어야만 존재하는데, static은 객체 생성 전부터 메모리에 올라가 있기 때문이다.

6-4. static의 메모리적 관점 (바이트코드와 실행)

우리가 앞서 배운 컴파일 과정과 연결해서 생각하면 static의 원리가 더 명확해진다.

  1. 컴파일: javac가 소스를 읽어 바이트코드(.class)를 만든다.
  2. 클래스 로딩: JVM이 실행될 때 클래스 로더가 이 바이트코드를 읽는다.
  3. static 할당: 이때 클래스 파일 안에 static으로 선언된 녀석들을 발견하면, 즉시 메서드 영역(Method Area)에 자리를 잡고 값을 초기화한다.

이 과정이 객체 생성(new)보다 먼저 일어나기 때문에, 우리가 new를 하지 않고도 System.out.println()처럼 바로 꺼내 쓸 수 있는 것이다.

6-5. static 사용 시 주의할 점

static은 편하지만 남용하면 독이 된다.

  1. 가비지 컬렉션(GC)의 관리 대상이 아니다: 일반 객체는 안 쓰면 GC가 치워버리지만, static은 프로그램이 꺼질 때까지 메모리에 박혀 있다. 너무 많이 만들면 메모리 부족 현상이 생길 수 있다.
  2. 객체지향(OOP)과 멀어진다: 데이터와 로직을 묶는 것이 객체지향인데, static은 데이터와 로직을 분리해버리는 경향이 있다.
  3. 멀티쓰레드 환경의 위험성: 모든 객체가 하나의 변수를 공유하므로, 여러 명이 동시에 값을 바꾸려 하면 데이터가 꼬일 수 있다. (동기화 이슈)

 

 

728x90

'회사 > 갤럭시아머니트리' 카테고리의 다른 글

[1부] CS 과제 공부하기  (0) 2026.01.21
서브쿼리와 조인 전략  (0) 2026.01.19