console은 브라우저의 개발자 도구(DevTools)에 메시지를 출력하는 JavaScript의 내장 객체이다. 이를 통해 우리는:
// 단순 값 출력
console.log("Hello, JavaScript!");
// 여러 값을 한 번에 출력
const name = "홍길동";
const age = 25;
#### console.log("이름:", name, "나이:", age);
// 객체 출력
const user = {
name: "홍길동",
age: 25,
email: "hong@example.com"
};
console.log(user);
// 배열 출력
const fruits = ["apple", "banana", "orange"];
console.log(fruits);
언제 쓸까? 변수 값을 확인하고 싶을 때, 프로그램이 특정 지점에 도달했는지 확인할 때
// 단순 에러 메시지
console.error("뭔가 잘못됐어요!");
// 에러 객체와 함께
try {
throw new Error("파일을 찾을 수 없습니다.");
} catch (error) {
console.error("에러 발생:", error.message);
}
특징: 개발자 도구에서 빨간색으로 표시되어 눈에 띈다.
언제 쓸까? 에러가 발생했음을 분명히 나타내고 싶을 때
// 잘못된 입력값 경고
const age = -5;
if (age < 0) {
console.warn("나이는 0 이상이어야 합니다. 입력된 값:", age);
}
// 더 이상 사용하지 않는 API 경고
function oldFunction() {
console.warn("이 함수는 더 이상 사용되지 않습니다. newFunction()을 사용하세요.");
}
특징: 개발자 도구에서 노란색으로 표시된다.
언제 쓸까? 에러는 아니지만 주의가 필요할 때
// 배열의 객체들을 표로 출력
const users = [
{ id: 1, name: "홍길동", age: 25 },
{ id: 2, name: "김영희", age: 28 },
{ id: 3, name: "이순신", age: 30 }
];
console.table(users);
// 특정 컬럼만 출력
console.table(users, ["name", "age"]);
// 객체도 표로 출력 가능
const person = {
name: "홍길동",
age: 25,
job: "개발자"
};
console.table(person);
특징: 데이터가 깔끔한 표 형태로 정렬되어 시각적으로 보기 좋다.
언제 쓸까? 복잡한 데이터 구조를 한눈에 파악하고 싶을 때
// 단순한 시간 측정
console.time("loop");
for (let i = 0; i < 1000000; i++) {
// 시간이 오래 걸리는 작업
}
console.timeEnd("loop");
// 출력: loop: 12.34ms
// 여러 구간 측정
console.time("총 실행 시간");
console.time("배열 생성");
const arr = new Array(100000).fill(0);
console.timeEnd("배열 생성");
console.time("배열 정렬");
arr.sort(() => Math.random() - 0.5);
console.timeEnd("배열 정렬");
console.timeEnd("총 실행 시간");
특징: 라벨로 구분된 여러 타이머를 동시에 실행할 수 있다.
언제 쓸까? 성능 최적화를 할 때 코드 실행 시간을 비교하고 싶을 때
// 단순 그룹
console.group("사용자 정보");
console.log("이름: 홍길동");
console.log("나이: 25");
console.log("직업: 개발자");
console.groupEnd();
// 축약 가능한 그룹
console.groupCollapsed("상세 정보");
console.log("전화: 010-1234-5678");
console.log("이메일: hong@example.com");
console.log("주소: 서울시 강남구");
console.groupEnd();
// 중첩된 그룹
console.group("회사 정보");
console.group("개발팀");
console.log("팀원: 5명");
console.log("프로젝트: 3개");
console.groupEnd();
console.group("디자인팀");
console.log("팀원: 3명");
console.log("프로젝트: 2개");
console.groupEnd();
console.groupEnd();
특징: groupCollapsed()를 사용하면 처음에는 닫혀있는 상태로 시작한다.
언제 쓸까? 관련된 여러 메시지를 정렬해서 보고 싶을 때
// 조건이 거짓일 때만 메시지 출력
const score = 60;
console.assert(score >= 80, "점수가 80점 이상이어야 합니다. 현재 점수:", score);
// 함수의 반환값 검증
function divide(a, b) {
console.assert(b !== 0, "0으로 나눌 수 없습니다");
return a / b;
}
divide(10, 0); // assert 조건이 거짓이므로 메시지 출력
divide(10, 2); // 조건이 참이므로 메시지 출력 안 함
// API 응답 검증
const response = { status: 200, data: null };
console.assert(response.status === 200, "API 요청이 실패했습니다:", response);
특징: 조건을 검사하고, 거짓일 때만 메시지를 출력한다.
언제 쓸까? 특정 조건이 만족되어야 할 때 검증하고 싶을 때
function grandparent() {
parent();
}
function parent() {
child();
}
function child() {
console.trace("호출 경로 추적");
}
grandparent();
// 출력:
// 호출 경로 추적
// child @ script.js:8
// parent @ script.js:4
// grandparent @ script.js:1
특징: 함수가 어떤 경로로 호출되었는지 알 수 있다.
언제 쓸까? 복잡한 코드에서 함수 호출 순서를 파악하고 싶을 때
import { useEffect, useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Counter 컴포넌트가 마운트되었습니다");
return () => {
console.log("Counter 컴포넌트가 언마운트되었습니다");
};
}, []);
useEffect(() => {
console.log("count가 변경되었습니다:", count);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
function UserCard({ userId, userName }) {
useEffect(() => {
console.table({ userId, userName });
}, [userId, userName]);
return <div>{userName}</div>;
}
function PostList() {
const [posts, setPosts] = useState([]);
useEffect(() => {
console.time("데이터 로딩");
fetch("/api/posts")
.then(res => res.json())
.then(data => {
console.log("받은 데이터:", data);
console.timeEnd("데이터 로딩");
setPosts(data);
})
.catch(error => {
console.error("데이터 로딩 실패:", error);
});
}, []);
return (
<div>
{posts.length > 0 ? (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
) : (
<p>로딩 중...</p>
)}
</div>
);
}
function LoginForm() {
const [form, setForm] = useState({ email: "", password: "" });
const handleChange = (e) => {
const { name, value } = e.target;
const newForm = { ...form, [name]: value };
setForm(newForm);
console.table(newForm);
};
return (
<form>
<input
type="email"
name="email"
value={form.email}
onChange={handleChange}
placeholder="이메일"
/>
<input
type="password"
name="password"
value={form.password}
onChange={handleChange}
placeholder="비밀번호"
/>
</form>
);
}
function DebugInfo() {
useEffect(() => {
if (process.env.NODE_ENV === "development") {
console.group("개발 환경 정보");
console.log("현재 환경:", process.env.NODE_ENV);
console.log("API URL:", process.env.REACT_APP_API_URL);
console.groupEnd();
}
}, []);
return <div>앱 컴포넌트</div>;
}
❌ 하지 말아야 할 것들
// ❌ 나쁜 예: 프로덕션에서도 출력됨
function getUser(id) {
const user = fetchUser(id);
console.log("사용자 정보:", user); // 민감한 정보 노출 위험!
return user;
}
// ✅ 좋은 예: 개발 환경에서만 출력
function getUser(id) {
const user = fetchUser(id);
if (process.env.NODE_ENV === "development") {
console.log("사용자 정보:", user);
}
return user;
}
// ❌ 나쁜 예: 비밀번호와 토큰 노출
const user = { email: "user@example.com", password: "secret123" };
console.log(user);
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
console.log("Token:", token);
// ✅ 좋은 예: 필요한 정보만 선택적으로 출력
const { email } = user;
console.log("사용자 이메일:", email);
// ❌ 나쁜 예: 루프 안에서 많은 console 호출
for (let i = 0; i < 1000000; i++) {
console.log("현재 i:", i); // 성능 저하!
}
// ✅ 좋은 예: 필요한 부분만 출력
for (let i = 0; i < 1000000; i++) {
if (i % 100000 === 0) {
console.log("현재 i:", i);
}
}
// .env.production에서
REACT_APP_DEBUG=false
// 컴포넌트에서
const debug = (message, data) => {
if (process.env.REACT_APP_DEBUG === "true") {
console.log(message, data);
}
};
✅ 좋은 습관들
// ❌ 나쁜 예
console.log(userData);
console.log(count);
// ✅ 좋은 예
console.log("사용자 정보:", userData);
console.log("현재 카운트:", count);
// 상황에 맞는 메서드 선택
console.log("일반 정보"); // 정보
console.warn("주의 필요"); // 경고
console.error("에러 발생"); // 에러
console.table(complexData); // 복잡한 데이터
// 재사용 가능한 디버그 함수
const logger = {
info: (message, data) => {
if (process.env.NODE_ENV === "development") {
console.log(`[INFO] ${message}`, data);
}
},
error: (message, error) => {
console.error(`[ERROR] ${message}`, error);
},
warn: (message, data) => {
console.warn(`[WARN] ${message}`, data);
}
};
// 사용
logger.info("데이터 로딩 시작", { url: "/api/users" });
logger.error("요청 실패", error);
// 필터링이 쉬운 접두사 사용
console.log("[UserData]", user);
console.log("[API]", response);
console.log("[State]", state);
// DevTools의 필터에 "API"라고 입력하면 [API] 메시지만 보임
console.log() - 일반 정보console.error() - 에러 (빨간색)console.warn() - 경고 (노란색)console.table() - 데이터를 표로 표시console.time() / timeEnd() - 실행 시간 측정console.group() / groupEnd() - 메시지 그룹화console.assert() - 조건부 출력console.trace() - 호출 경로 추적console을 제대로 사용하면 버그를 빠르게 찾을 수 있고, 코드의 흐름을 쉽게 이해할 수 있다. 이제 console의 다양한 기능을 활용해서 더 효율적으로 디버깅해보자!🚀