String
문자열
문자(character)의 시퀀스로 구성된 텍스트 데이터 타입. 대부분의 언어에서 불변(Immutable)하며, 다양한 조작 메서드를 제공합니다.
문자열
문자(character)의 시퀀스로 구성된 텍스트 데이터 타입. 대부분의 언어에서 불변(Immutable)하며, 다양한 조작 메서드를 제공합니다.
String(문자열)은 문자(character)들의 순차적 시퀀스로 구성된 기본 데이터 타입입니다. 프로그래밍에서 텍스트 데이터를 표현하고 처리하는 핵심 자료구조로, 사용자 입력, 파일 내용, API 응답, 데이터베이스 레코드 등 거의 모든 곳에서 사용됩니다. 내부적으로는 문자 배열(char array)이나 바이트 시퀀스로 구현되며, 언어마다 표현 방식이 다릅니다.
불변성(Immutability)은 String의 핵심 특성입니다. Java, Python, JavaScript, Go 등 대부분의 현대 언어에서 String은 한 번 생성되면 변경할 수 없습니다. "hello"에 " world"를 더하면 기존 문자열이 수정되는 것이 아니라 새로운 문자열 "hello world"가 메모리에 생성됩니다. 이 특성은 스레드 안전성, 해시 키 사용, 문자열 인터닝(String Pool) 등의 이점을 제공합니다.
문자 인코딩은 문자를 바이트로 변환하는 방식입니다. ASCII는 영문자를 1바이트(0-127)로 표현하고, UTF-8은 가변 길이(1-4바이트)로 전 세계 문자를 표현합니다. UTF-16은 대부분의 문자를 2바이트로, 일부를 4바이트로 표현합니다. JavaScript와 Java는 내부적으로 UTF-16을 사용하고, Python 3와 Go는 UTF-8을 기본으로 합니다. 인코딩 불일치는 "깨진 문자" 버그의 주요 원인입니다.
실무 고려사항으로 문자열 연결 성능이 중요합니다. 반복문에서 += 연산은 매번 새 객체를 생성하여 O(n²) 시간복잡도가 됩니다. 대신 Java의 StringBuilder, Python의 "".join(list), JavaScript의 배열 join()을 사용하면 O(n)으로 개선됩니다. 대용량 텍스트 처리 시 메모리 효율을 위해 스트리밍 방식이나 Buffer를 활용합니다.
// 1. 기본 문자열 조작
const str = "Hello, World!";
console.log(str.length); // 13
console.log(str.toUpperCase()); // "HELLO, WORLD!"
console.log(str.slice(0, 5)); // "Hello"
console.log(str.split(", ")); // ["Hello", "World!"]
console.log(str.includes("World")); // true
console.log(str.indexOf("o")); // 4
// 2. 템플릿 리터럴 (ES6+)
const name = "김개발";
const age = 28;
const greeting = `안녕하세요, ${name}님!
나이: ${age}세
내년 나이: ${age + 1}세`;
console.log(greeting);
// 3. 정규식 활용
const email = "user@example.com";
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(emailRegex.test(email)); // true
const text = "가격: 10,000원, 수량: 5개";
const numbers = text.match(/\d+/g); // ["10", "000", "5"]
const cleaned = text.replace(/,/g, ""); // 쉼표 제거
// 4. 효율적인 문자열 연결
const items = ["사과", "바나나", "오렌지"];
// 비효율적 (매번 새 문자열 생성)
let bad = "";
for (const item of items) {
bad += item + ", "; // O(n²)
}
// 효율적 (배열 join 사용)
const good = items.join(", "); // O(n)
console.log(good); // "사과, 바나나, 오렌지"
// 5. 유니코드 처리
const emoji = "👨👩👧👦";
console.log(emoji.length); // 11 (서로게이트 페어 + ZWJ)
console.log([...emoji].length); // 1 (스프레드로 제대로 분리)
// 한글 자모 분리
const korean = "한글";
console.log(korean.normalize("NFD").length); // 6 (자모 분리)
console.log(korean.normalize("NFC").length); // 2 (완성형)
# 1. 기본 문자열 조작
text = "Hello, World!"
print(len(text)) # 13
print(text.upper()) # "HELLO, WORLD!"
print(text[0:5]) # "Hello" (슬라이싱)
print(text.split(", ")) # ["Hello", "World!"]
print("World" in text) # True
print(text.find("o")) # 4
# 2. f-string (Python 3.6+)
name = "김개발"
age = 28
greeting = f"""안녕하세요, {name}님!
나이: {age}세
내년 나이: {age + 1}세"""
print(greeting)
# 디버깅용 (Python 3.8+)
print(f"{name=}, {age=}") # name='김개발', age=28
# 3. 정규식 활용
import re
email = "user@example.com"
pattern = r'^[^\s@]+@[^\s@]+\.[^\s@]+$'
print(bool(re.match(pattern, email))) # True
text = "가격: 10,000원, 수량: 5개"
numbers = re.findall(r'\d+', text) # ['10', '000', '5']
cleaned = re.sub(r',', '', text) # 쉼표 제거
# 4. 효율적인 문자열 연결
items = ["사과", "바나나", "오렌지"]
# 비효율적 (매번 새 문자열 생성)
bad = ""
for item in items:
bad += item + ", " # O(n²)
# 효율적 (join 사용)
good = ", ".join(items) # O(n)
print(good) # "사과, 바나나, 오렌지"
# 대용량 처리: StringIO 사용
from io import StringIO
buffer = StringIO()
for item in items:
buffer.write(item)
buffer.write(", ")
result = buffer.getvalue()
# 5. 인코딩/디코딩
korean = "한글"
encoded = korean.encode('utf-8') # b'\xed\x95\x9c\xea\xb8\x80'
decoded = encoded.decode('utf-8') # "한글"
# 인코딩 오류 처리
text_with_error = b'\xff\xfe'
safe = text_with_error.decode('utf-8', errors='replace') # 깨진 문자 대체
// 1. 기본 문자열 조작
String str = "Hello, World!";
System.out.println(str.length()); // 13
System.out.println(str.toUpperCase()); // "HELLO, WORLD!"
System.out.println(str.substring(0, 5)); // "Hello"
System.out.println(str.split(", ")[0]); // "Hello"
System.out.println(str.contains("World")); // true
System.out.println(str.indexOf("o")); // 4
// 2. 문자열 비교 (== vs equals)
String a = "hello";
String b = "hello";
String c = new String("hello");
System.out.println(a == b); // true (String Pool)
System.out.println(a == c); // false (다른 객체)
System.out.println(a.equals(c)); // true (내용 비교)
// 3. 정규식 활용
import java.util.regex.*;
String email = "user@example.com";
boolean isValid = email.matches("^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$");
String text = "가격: 10,000원";
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println(matcher.group()); // 10, 000
}
// 4. 효율적인 문자열 연결 (StringBuilder)
String[] items = {"사과", "바나나", "오렌지"};
// 비효율적 (매번 새 String 생성)
String bad = "";
for (String item : items) {
bad += item + ", "; // O(n²) - 내부적으로 StringBuilder 생성
}
// 효율적 (StringBuilder 재사용)
StringBuilder sb = new StringBuilder();
for (String item : items) {
if (sb.length() > 0) sb.append(", ");
sb.append(item);
}
String good = sb.toString(); // O(n)
// Java 8+ String.join
String joined = String.join(", ", items);
// 5. Text Block (Java 15+)
String json = """
{
"name": "김개발",
"age": 28,
"skills": ["Java", "Spring"]
}
""";
// 6. 인코딩 처리
import java.nio.charset.StandardCharsets;
String korean = "한글";
byte[] utf8Bytes = korean.getBytes(StandardCharsets.UTF_8);
String decoded = new String(utf8Bytes, StandardCharsets.UTF_8);
"이 for 루프에서 문자열 += 연산이 1만 번 반복되고 있네요. String이 불변이라 매번 새 객체가 생성되어서 O(n²)이 됩니다. StringBuilder로 바꾸거나 String.join() 쓰면 O(n)으로 개선되고, 제 테스트에서는 실행 시간이 2초에서 50ms로 줄었어요."
"String의 불변성은 세 가지 이점이 있습니다. 첫째, 스레드 안전성 - 여러 스레드가 동시에 읽어도 안전합니다. 둘째, 해시맵 키로 사용 가능 - hashCode가 변하지 않으니까요. 셋째, String Pool을 통한 메모리 절약 - 같은 리터럴은 하나의 객체만 생성됩니다. 단점은 수정할 때마다 새 객체가 생성되어 GC 부담이 늘어나는 것인데, 이건 StringBuilder로 해결합니다."
"한글이 깨지는 이유를 찾았습니다. 파일은 EUC-KR로 저장되어 있는데 서버에서 UTF-8로 읽고 있었어요. 파일을 UTF-8로 재저장하거나, InputStreamReader에 'EUC-KR' charset을 명시하면 해결됩니다. 앞으로는 모든 파일을 UTF-8 BOM 없이 저장하는 걸로 가이드 업데이트하죠."
불변 String을 += 로 연결하면 매번 새 객체 생성 (O(n²)). StringBuilder, String.join(), 또는 언어별 권장 방식 사용. 1만 번 루프에서 100배 이상 성능 차이.
파일 읽기/쓰기, HTTP 응답에서 인코딩 생략 시 시스템 기본값 사용 → 환경마다 다른 결과. 항상 UTF-8 명시적 지정. Content-Type 헤더에 charset=utf-8 포함.
사용자 입력을 직접 SQL에 연결 ("SELECT * FROM users WHERE name='" + input + "'")하면 공격에 노출. 반드시 Prepared Statement나 ORM의 파라미터 바인딩 사용.
StringBuilder/join으로 효율적 연결, 명시적 인코딩 지정, Prepared Statement로 SQL 주입 방지, == 대신 equals()로 내용 비교 (Java), 유니코드 길이는 [...str].length 사용.