본문으로 건너뛰기
Previous
Next
프로그래밍 언어별 흔한 에러 해결 가이드 | C++, Python, Java, JavaScript

프로그래밍 언어별 흔한 에러 해결 가이드 | C++, Python, Java, JavaScript

프로그래밍 언어별 흔한 에러 해결 가이드 | C++, Python, Java, JavaScript

이 글의 핵심

C++, Python, Java, JavaScript에서 자주 발생하는 에러와 해결 방법을 정리합니다. 컴파일 에러, 런타임 에러, 논리 에러를 체계적으로 해결하는 방법을 제시합니다.

들어가며: 에러는 배움의 기회

”에러 메시지가 무슨 뜻인지 모르겠어요”

프로그래밍을 하다 보면 수많은 에러를 만나게 됩니다. 에러는 버그가 아니라 컴파일러나 런타임이 보내는 힌트입니다. 이 글에서 다루는 것:

  • 언어별 흔한 에러와 해결법
  • 에러 메시지 읽는 법
  • 체계적인 디버깅 방법
  • 예방 전략

실전 경험에서 배운 교훈

이 기술을 실무 프로젝트에 처음 도입했을 때, 공식 문서만으로는 알 수 없었던 많은 함정들이 있었습니다. 특히 프로덕션 환경에서 발생하는 엣지 케이스들은 로컬 개발 환경에서는 재현조차 되지 않았죠.

가장 어려웠던 점은 성능 최적화였습니다. 처음엔 “동작만 하면 되겠지”라고 생각했지만, 실제 사용자 트래픽이 몰리면서 병목 지점들이 하나씩 드러났습니다. 특히 데이터베이스 쿼리 최적화, 캐싱 전략, 에러 핸들링 구조 등은 여러 번의 장애를 겪으면서 개선해 나갔습니다.

이 글에서는 그런 시행착오를 통해 얻은 실전 노하우와, “이렇게 하면 안 된다”는 교훈들을 함께 정리했습니다. 특히 트러블슈팅 섹션은 실제 장애 대응 경험을 바탕으로 작성했으니, 비슷한 문제를 마주했을 때 참고하시면 도움이 될 것입니다.

1. C++ 흔한 에러

1.1 Segmentation Fault (세그멘테이션 폴트)

에러 메시지:

Segmentation fault (core dumped)

원인:

  • 널 포인터 역참조
  • 배열 범위 초과
  • 해제된 메모리 접근
  • 스택 오버플로 예제:
// 원인 1: 널 포인터 역참조
int* ptr = nullptr;
*ptr = 10;  // ❌ Segmentation fault
// 해결
if (ptr != nullptr) {
    *ptr = 10;
}
// 원인 2: 배열 범위 초과
int arr[5] = {1, 2, 3, 4, 5};
arr[10] = 100;  // ❌ 미정의 동작, Segfault 가능
// 해결: vector 사용 (at()으로 범위 체크)
std::vector<int> vec = {1, 2, 3, 4, 5};
try {
    vec.at(10) = 100;  // ✅ 예외 발생
} catch (const std::out_of_range& e) {
    std::cerr << "범위 초과: " << e.what() << std::endl;
}
// 원인 3: 댕글링 포인터 (해제된 메모리 접근)
int* ptr2 = new int(10);
delete ptr2;
*ptr2 = 20;  // ❌ 이미 해제된 메모리
// 해결: 스마트 포인터 사용
std::unique_ptr<int> smart_ptr = std::make_unique<int>(10);
// 자동 해제, 댕글링 포인터 방지

디버깅 도구:

# GDB로 디버깅
g++ -g main.cpp -o main
gdb ./main
(gdb) run
(gdb) backtrace  # 크래시 위치 확인
# Valgrind로 메모리 에러 검사
valgrind --leak-check=full ./main
# AddressSanitizer (ASan)
g++ -fsanitize=address -g main.cpp -o main
./main

1.2 Undefined Reference (링커 에러)

에러 메시지:

undefined reference to `myFunction()'

원인:

  • 함수 선언만 있고 정의가 없음
  • 라이브러리 링크 누락
  • 네임스페이스 불일치 예제:
// header.h
void myFunction();
// main.cpp
#include "header.h"
int main() {
    myFunction();  // ❌ undefined reference
    return 0;
}
// 해결: 정의 추가
// impl.cpp
#include "header.h"
void myFunction() {
    // 구현
}
// 컴파일
g++ main.cpp impl.cpp -o main  // ✅

라이브러리 링크 누락:

# 에러
g++ main.cpp -o main
# undefined reference to `pthread_create'
# 해결: -lpthread 추가
g++ main.cpp -o main -lpthread

1.3 Template 에러

에러 메시지:

error: no matching function for call to 'std::vector<int>::push_back(std::string)'

원인: 템플릿 타입 불일치 예제:

std::vector<int> vec;
vec.push_back("hello");  // ❌ 타입 에러
// 해결
std::vector<std::string> vec;
vec.push_back("hello");  // ✅

2. Python 흔한 에러

2.1 IndentationError

에러 메시지:

IndentationError: expected an indented block

원인: 들여쓰기 불일치 예제:

# ❌ 에러
def greet():
print("Hello")  # 들여쓰기 없음
# ✅ 해결
def greet():
    print("Hello")
# ❌ 탭과 스페이스 혼용
def calculate():
    x = 10  # 스페이스 4칸
	y = 20  # 탭 1개
    return x + y  # IndentationError
# ✅ 해결: 스페이스로 통일
def calculate():
    x = 10
    y = 20
    return x + y

예방:

  • 에디터 설정: 스페이스 4칸
  • .editorconfig 사용
# .editorconfig
[*.py]
indent_style = space
indent_size = 4

2.2 NameError

에러 메시지:

NameError: name 'x' is not defined

원인: 변수를 정의하기 전에 사용 예제:

# ❌ 에러
print(x)  # x가 정의되지 않음
# ✅ 해결
x = 10
print(x)
# ❌ 함수 내부에서 전역 변수 수정
count = 0
def increment():
    count += 1  # UnboundLocalError
# ✅ 해결: global 키워드
count = 0
def increment():
    global count
    count += 1

2.3 TypeError

에러 메시지:

TypeError: unsupported operand type(s) for +: 'int' and 'str'

원인: 타입 불일치 예제:

# ❌ 에러
result = 10 + "20"  # int + str
# ✅ 해결
result = 10 + int("20")  # 30
result = str(10) + "20"  # "1020"
# ❌ None 타입 연산
def get_value():
    return None
x = get_value()
y = x + 10  # TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
# ✅ 해결: None 체크
x = get_value()
if x is not None:
    y = x + 10

2.4 IndexError

에러 메시지:

IndexError: list index out of range

원인: 리스트 범위 초과 예제:

# ❌ 에러
arr = [1, 2, 3]
print(arr[5])  # IndexError
# ✅ 해결: 범위 체크
if len(arr) > 5:
    print(arr[5])
else:
    print("인덱스 범위 초과")
# ✅ 또는 try-except
try:
    print(arr[5])
except IndexError:
    print("인덱스 범위 초과")

3. Java 흔한 에러

3.1 NullPointerException

에러 메시지:

java.lang.NullPointerException

원인: null 객체의 메서드 호출 예제:

// ❌ 에러
String str = null;
int len = str.length();  // NullPointerException
// ✅ 해결: null 체크
String str = null;
if (str != null) {
    int len = str.length();
}
// ✅ Java 8+ Optional 사용
Optional<String> opt = Optional.ofNullable(str);
int len = opt.map(String::length).orElse(0);
// ✅ Java 14+ 패턴 매칭
if (str instanceof String s) {
    int len = s.length();
}

3.2 ClassCastException

에러 메시지:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

원인: 잘못된 타입 캐스팅 예제:

// ❌ 에러
Object obj = "Hello";
Integer num = (Integer) obj;  // ClassCastException
// ✅ 해결: instanceof 체크
Object obj = "Hello";
if (obj instanceof Integer) {
    Integer num = (Integer) obj;
} else {
    System.out.println("Integer가 아닙니다");
}
// ✅ 제네릭 사용 (타입 안전)
List<Integer> list = new ArrayList<>();
list.add(10);
Integer num = list.get(0);  // 캐스팅 불필요

3.3 ConcurrentModificationException

에러 메시지:

java.util.ConcurrentModificationException

원인: 순회 중 컬렉션 수정 예제:

// ❌ 에러
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
for (Integer num : list) {
    if (num % 2 == 0) {
        list.remove(num);  // ConcurrentModificationException
    }
}
// ✅ 해결: Iterator 사용
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
    Integer num = it.next();
    if (num % 2 == 0) {
        it.remove();
    }
}
// ✅ 또는 removeIf 사용 (Java 8+)
list.removeIf(num -> num % 2 == 0);

4. JavaScript 흔한 에러

4.1 TypeError: Cannot read property of undefined

에러 메시지:

TypeError: Cannot read property 'name' of undefined

원인: undefined 객체의 속성 접근 예제:

// ❌ 에러
const user = undefined;
console.log(user.name);  // TypeError
// ✅ 해결: Optional Chaining (ES2020)
console.log(user?.name);  // undefined (에러 안 남)
// ✅ 기본값 설정
const name = user?.name ?? 'Guest';
console.log(name);  // 'Guest'
// ✅ 명시적 체크
if (user && user.name) {
    console.log(user.name);
}

4.2 ReferenceError: x is not defined

에러 메시지:

ReferenceError: x is not defined

원인: 선언되지 않은 변수 사용 예제:

// ❌ 에러
console.log(x);  // ReferenceError
// ✅ 해결
const x = 10;
console.log(x);
// ❌ 호이스팅 문제
console.log(y);  // undefined (var는 호이스팅)
var y = 10;
// ✅ let/const 사용 (TDZ)
console.log(z);  // ReferenceError
const z = 10;

4.3 Promise 에러

에러 메시지:

UnhandledPromiseRejectionWarning: Error: Failed to fetch

원인: Promise rejection 미처리 예제:

// ❌ 에러 (catch 없음)
fetch('https://api.example.com/data')
  .then(res => res.json())
  .then(data => console.log(data));
// 네트워크 에러 시 UnhandledPromiseRejection
// ✅ 해결: catch 추가
fetch('https://api.example.com/data')
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.error('에러:', err));
// ✅ async/await 사용
async function fetchData() {
  try {
    const res = await fetch('https://api.example.com/data');
    const data = await res.json();
    console.log(data);
  } catch (err) {
    console.error('에러:', err);
  }
}

4.4 this 바인딩 문제

에러 메시지:

TypeError: Cannot read property 'name' of undefined

원인: this가 예상과 다른 객체를 가리킴 예제:

// ❌ 에러
class Person {
  constructor(name) {
    this.name = name;
  }
  
  greet() {
    console.log(`Hello, ${this.name}`);
  }
}
const person = new Person('Alice');
setTimeout(person.greet, 1000);  // TypeError: this is undefined
// ✅ 해결 1: 화살표 함수
setTimeout(() => person.greet(), 1000);
// ✅ 해결 2: bind
setTimeout(person.greet.bind(person), 1000);
// ✅ 해결 3: 클래스 필드 (화살표 함수)
class Person {
  constructor(name) {
    this.name = name;
  }
  
  greet = () => {
    console.log(`Hello, ${this.name}`);
  }
}

5. 디버깅 전략

체계적 디버깅 프로세스

flowchart TD
    A[에러 발생] --> B[에러 메시지 읽기]
    B --> C{컴파일 에러?}
    C -->|예| D[문법/타입 체크]
    C -->|아니오| E{런타임 에러?}
    E -->|예| F[스택 트레이스 확인]
    E -->|아니오| G[논리 에러]
    
    D --> H[에러 위치 수정]
    F --> I[디버거로 추적]
    G --> J[로그/테스트 추가]
    
    H --> K[재컴파일]
    I --> L[원인 파악]
    J --> L
    L --> M[수정 및 테스트]

디버깅 도구 비교

언어디버거로깅테스트
C++GDB, LLDB, Visual Studio Debuggerspdlog, glogGoogle Test, Catch2
Pythonpdb, ipdblogging, logurupytest, unittest
JavaIntelliJ Debugger, jdbLog4j, SLF4JJUnit, TestNG
JavaScriptChrome DevTools, VS Codeconsole, winstonJest, Mocha

디버깅 팁

1. 에러 메시지를 정확히 읽기

❌ "에러가 났어요" (너무 모호)
✅ "TypeError: Cannot read property 'name' of undefined at line 42" (구체적)

2. 최소 재현 코드 만들기

// 복잡한 코드에서 에러 발생
// → 최소한의 코드로 재현
// 원본 (100줄)
function complexFunction() {
  // ....복잡한 로직
  const result = data.map(x => x.value).filter(x => x > 10);
  // ....더 복잡한 로직
}
// 최소 재현 (5줄)
const data = [{ value: 5 }, { value: 15 }];
const result = data.map(x => x.value).filter(x => x > 10);
console.log(result);  // [15]

3. 이분 탐색으로 원인 찾기

# 코드가 100줄인데 어디서 에러가 나는지 모름
# → 절반씩 주석 처리하며 범위 좁히기
# 1-50줄 주석 → 에러 안 남 → 1-50줄에 원인
# 1-25줄 주석 → 에러 남 → 26-50줄에 원인
# 26-37줄 주석 → 에러 안 남 → 26-37줄에 원인
# ...

4. 로그 추가

// C++
#include <iostream>
void process(int x) {
    std::cout << "DEBUG: x = " << x << std::endl;
    // ....로직
}
# Python
import logging
logging.basicConfig(level=logging.DEBUG)
def process(x):
    logging.debug(f"x = {x}")
    # ....로직

5. 디버거 사용

# Python pdb
python -m pdb script.py
# Node.js
node --inspect-brk script.js
# Chrome에서 chrome://inspect 열기

6. 정리

핵심 요약

C++ 흔한 에러:

  • Segmentation Fault: 메모리 접근 에러
  • Undefined Reference: 링커 에러
  • Template 에러: 타입 불일치 Python 흔한 에러:
  • IndentationError: 들여쓰기 불일치
  • NameError: 변수 미정의
  • TypeError: 타입 불일치 Java 흔한 에러:
  • NullPointerException: null 접근
  • ClassCastException: 잘못된 캐스팅
  • ConcurrentModificationException: 순회 중 수정 JavaScript 흔한 에러:
  • TypeError: undefined 접근
  • ReferenceError: 변수 미정의
  • Promise rejection: 비동기 에러 미처리

디버깅 체크리스트

  • 에러 메시지 정확히 읽기
  • 스택 트레이스 확인
  • 최소 재현 코드 만들기
  • 로그 추가
  • 디버거 사용
  • 테스트 코드 작성

다음 단계

각 언어의 자세한 디버깅 방법은 아래 글을 참고하세요:

심화 부록: 구현·운영 관점

이 부록은 앞선 본문에서 다룬 주제(「프로그래밍 언어별 흔한 에러 해결 가이드 | C++, Python, Java, JavaScript」)를 구현·런타임·운영 관점에서 다시 압축합니다. 도메인별 세부 구현은 글마다 다르지만, 입력 검증 → 핵심 연산 → 부작용(I/O·네트워크·동시성) → 관측의 흐름으로 장애를 나누면 원인 추적이 빨라집니다.

내부 동작과 핵심 메커니즘

flowchart TD
  A[입력·요청·이벤트] --> B[파싱·검증·디코딩]
  B --> C[핵심 연산·상태 전이]
  C --> D[부작용: I/O·네트워크·동시성]
  D --> E[결과·관측·저장]
sequenceDiagram
  participant C as 클라이언트/호출자
  participant B as 경계(런타임·게이트웨이·프로세스)
  participant D as 의존성(API·DB·큐·파일)
  C->>B: 요청/이벤트
  B->>D: 조회·쓰기·RPC
  D-->>B: 지연·부분 실패·재시도 가능
  B-->>C: 응답 또는 오류(코드·상관 ID)
  • 불변 조건(Invariant): 버퍼 경계, 프로토콜 상태, 트랜잭션 격리, FD 상한 등 단계별로 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
  • 결정성: 순수 층과 시간·네트워크·스케줄에 의존하는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
  • 경계 비용: 직렬화, 인코딩, syscall 횟수, 락 경합, 할당·GC, 캐시 미스를 의심 목록에 둡니다.
  • 백프레셔: 생산자가 소비자보다 빠를 때 버퍼·큐·스트림에서 속도를 줄이는 신호를 어디에 둘지 정의합니다.

프로덕션 운영 패턴

영역운영 관점 질문
관측성요청 단위 상관 ID, 에러율·지연 p95/p99, 의존성 타임아웃·재시도가 대시보드에 보이는가
안전성입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가
신뢰성재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가
성능캐시·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가
배포롤백 룬북, 카나리/블루그린, 마이그레이션·피처 플래그가 문서화되어 있는가
용량피크 트래픽·디스크·FD·스레드 풀 상한을 주기적으로 검증하는가

스테이징은 데이터 양·네트워크 RTT·동시성을 프로덕션에 가깝게 맞출수록 재현율이 올라갑니다.

확장 예시: 엔드투엔드 미니 시나리오

앞선 본문 주제(「프로그래밍 언어별 흔한 에러 해결 가이드 | C++, Python, Java, JavaScript」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.

  1. 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
  2. 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
  3. 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
  4. 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
  5. 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값을 점검한다.
handle(request):
  ctx = newCorrelationId()
  validated = validateSchema(request)
  authorize(validated, ctx)
  result = domainCore(validated)
  persistOrEmit(result, idempotentKey)
  recordMetrics(ctx, latency, outcome)
  return result

문제 해결(Troubleshooting)

증상가능 원인조치
간헐적 실패레이스, 타임아웃, 외부 의존성, DNS최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검
성능 저하N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거
메모리 증가캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납상한·TTL·힙/FD 스냅샷 비교
빌드·배포만 실패환경 변수, 권한, 플랫폼 차이, lockfileCI 로그와 로컬 diff, 런타임·이미지 버전 핀
설정 불일치프로필·시크릿·기본값, 리전스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화
데이터 불일치비멱등 재시도, 부분 쓰기, 캐시 무효화 누락멱등 키·아웃박스·트랜잭션 경계 재검토

권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.

배포 전에는 git addgit commitgit pushnpm run deploy 순서를 권장합니다.


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. C++, Python, Java, JavaScript에서 자주 발생하는 에러와 해결 방법을 정리합니다. 컴파일 에러, 런타임 에러, 논리 에러를 체계적으로 해결하는 방법을 제시합니다. Start now. 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

디버깅, 에러해결, C++, Python, Java, JavaScript, 트러블슈팅, 개발팁 등으로 검색하시면 이 글이 도움이 됩니다.