Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions fundamentals/debug/pages/diagnose/map.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,25 @@
아래 코드에는 치명적인 오류가 있는데요, 작업 지도를 하나씩 검증하며 어디에 문제가 발생하는지 살펴보아요.

```tsx 8,15,19,26
import React, { useState } from "react";
import { useState } from "react";

const ValidatedInput = () => {
function ValidatedInput() {
const [input, setInput] = useState("");
const [isValid, setIsValid] = useState(true);

const validate = (value) => {
//2. 입력이 바뀔 때마다 유효성 검사 함수가 실행돼요:"
// 2. 입력이 바뀔 때마다 유효성 검사 함수가 실행돼요
const valid = value.length < 5;
return valid;
};

const handleChange = (e) => {
const value = e.target.value;
//1. 사용자가 검색 입력창에 키워드를 입력해요
// 1. 사용자가 검색 입력창에 키워드를 입력해요
setInput(value);

const result = validate(value);
//3. 검사 결과에 따라 true/false를 반환해요
// 3. 검사 결과에 따라 true/false를 반환해요
setIsValid(result);
};

Expand All @@ -60,15 +60,15 @@ const ValidatedInput = () => {
{isValid && <div style={{ color: "red" }}>Error message</div>}
</div>
);
};
}
export default ValidatedInput;
```

![](../../images/diagnose/map-check.png)

## 에러 도출

검증 결과, `isValid &&` 조건으로 작성된 에러 UI 렌더링 부분에 **논리 오류**가 있었어요.`isValid === false`로 작성해야 하는데, `isValid &&`로 되어 있어서, **정상적인 UI가 아닌, 잘못된 조건에 의해 오류 메시지가 렌더링**되고 있었어요.
검증 결과, `isValid &&` 조건으로 작성된 에러 UI 렌더링 부분에 **논리 오류**가 있었어요.`isValid === false &&`로 작성해야 하는데, `isValid &&`로 되어 있어서, **정상적인 UI가 아닌, 잘못된 조건에 의해 오류 메시지가 렌더링**되고 있었어요.

```tsx 4
return (
Expand Down
4 changes: 2 additions & 2 deletions fundamentals/debug/pages/fix/correct.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ console.log(selectedUser?.name ?? "사용자 없음");
```tsx
function SearchBox() {
const [query, setQuery] = useState("");
const [result, setResult] = useState("");
const [results, setResults] = useState([]);

useEffect(() => {
if (!query) return;

fetch(`/api/search?q=${query}`)
.then((res) => res.json())
.then((data) => {
setResult(data);
setResults(data);
});
}, [query]);

Expand Down
2 changes: 1 addition & 1 deletion fundamentals/debug/pages/fix/dead-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ A/B 테스트가 끝났는데 분기 코드가 남아 있으면, 독자가 “

```tsx
export function RecommendationBanner({ variant }: { variant: "A" | "B" }) {
//TODO:: 종료된 실험 제거 (실험안 A으로 종료)
//TODO: 종료된 실험 제거 (실험안 A로 종료)
if (variant === "A") {
return <BannerA />;
} else {
Expand Down
14 changes: 8 additions & 6 deletions fundamentals/debug/pages/fix/pure.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ function OrderSummary({

## 예시: React Hook을 활용해 팝업창을 보여줄 때

react의 hook예시도 들어볼게요. 모달을 보여주는 로직이에요.
react의 hook 예시도 들어볼게요. 모달을 보여주는 로직이에요.

### 기존코드

`localStorage`, `isTodayShown`등과 같은 여러 로직이 `useEffect` 내부에 흩어져 있어 모듈화되어 있지 않아요. 그래서 가독성도 떨어지고, 테스트도 어려워요3.
`localStorage`, `isTodayShown`등과 같은 여러 로직이 `useEffect` 내부에 흩어져 있어 모듈화되어 있지 않아요. 그래서 가독성도 떨어지고, 테스트도 어려워요.

```tsx
const STORAGE_KEY = "notification-modal-shownAt";
Expand Down Expand Up @@ -141,6 +141,8 @@ export function setLocalStorageValue(key: string, value: string): void {
**utils/modal.ts**

```tsx
import { getLocalStorageValue, setLocalStorageValue } from "./localStorage";

export function getIsModalShownToday(modalKey: string) {
const lastShown = getLocalStorageValue(modalKey);
const todayDateString = new Date().toDateString();
Expand All @@ -158,7 +160,7 @@ export function setModalShownToday(modalKey: string) {

```tsx
import { useEffect, useState } from "react";
import { useNotificationAgreementState } from "./useNotificationAgreementState"; // 필요 시 경로 조정
import { getIsModalShownToday, setModalShownToday } from "./utils/modal";

const MODAL_KEY = "test1";
export function useIsModalShow() {
Expand All @@ -170,7 +172,7 @@ export function useIsModalShow() {
setModalShownToday(MODAL_KEY);
setShowModal(true);
}
}, [isAgreed]);
}, []);

const close = () => {
setShowModal(false);
Expand All @@ -186,15 +188,15 @@ export function useIsModalShow() {
**HomePage.tsx**

```tsx
import { useIsModalShow } from './hooks/useIsModalShow';
import { useIsModalShow } from "./hooks/useIsModalShow";

function HomePage() {
const { showModal, close } = useIsModalShow();

return (
<>
<h1>Welcome!</h1>
<Modal isOpen={showModal} onAgree={agree} onClose={close} />}
<Modal isOpen={showModal} onClose={close} />
</>
);
}
Expand Down
4 changes: 2 additions & 2 deletions fundamentals/debug/pages/prevent/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ module.exports = {
};
```

## 린린트 룰에 더미텍스트 확인 추가
## 린트 룰에 더미텍스트 확인 추가

### 문제

UI 개발 도중 사용한 더미 이름, 가짜 텍스트(“김토스님”, “홍길동”, “ㅇㅇㅇ” 등) 이 실제 PR에 포함돼 배포되거나 유저에게 노출되는 경우가 있어요. 특히 QA 환경에서는 가짜 이름이 버그처럼 보여 혼란을 유발하고, 실무에서 신뢰도를 해칠 수 있어요.

### 해결방법

ESLint로 특정 문자열(“김토스”, “홍길동”, “테스트용” 등) 포함 시 에러 발생하도록 설정해요. `no-restricted-syntax`는특정 구문(Syntax)을 아예 사용하지 못하도록 제한하는 ESLint 규칙이에요.
ESLint로 특정 문자열(“김토스”, “홍길동”, “테스트용” 등) 포함 시 에러 발생하도록 설정해요. `no-restricted-syntax`는 특정 구문(Syntax)을 아예 사용하지 못하도록 제한하는 ESLint 규칙이에요.

```js 6
module.exports = {
Expand Down
6 changes: 3 additions & 3 deletions fundamentals/debug/pages/reproduce/repeat.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
예를 들어, 동일한 요청이 여러 번 반복될 때 비정상적인 화면이 보여지는 버그 리포트를 받았다고 가정해 볼게요. 이 문제를 재현하기 위해, 다음처럼 **자동 클릭 함수**를 만들어 반복적인 클릭 상황을 만들어 볼 수 있어요. 그리고 디버거를 걸어서 중간 중간 값이 어떻게 변경되는지 추적해볼 수 있어요.

```tsx
import React, { useState } from "react";
import { useState } from "react";

function simulateRapidClicks(
target: HTMLElement,
Expand All @@ -26,7 +26,7 @@ function simulateRapidClicks(
}, interval);
}

const DoubleClickTest = () => {
function DoubleClickTest() {
const [count, setCount] = useState(0);
const [disabled, setDisabled] = useState(false);

Expand All @@ -53,7 +53,7 @@ const DoubleClickTest = () => {
<button onClick={handleAutoClick}>자동 광클 재현</button>
</div>
);
};
}

export default DoubleClickTest;
```
Expand Down
21 changes: 12 additions & 9 deletions fundamentals/debug/pages/reproduce/simply.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@

예를 들어, 숫자 배열에서 짝수만 필터링하는 로직에 문제가 생겼다고 가정해볼게요. 원래 코드에서는 컴포넌트에서 하는 일이 많아서 어디서부터 문제가 발생한 건지 알기 어려워요.

현재 컴포넌트에서는 데이터 fetching, 필터링 로직, 상태 업데이트, 사용자 이벤트 처리가 하나의 함수 안에 모여 있어요. 이처럼 책임이 분리되지 않으면, 특정 결과(filteredNumbers)가 잘못되었을 때 어떤 로직에서 문제가 발생했는지 파악하기가 어려워요. 특히 여러 상태 변화가 엮여 있을 경우, 상태 간의 상호작용이나 렌더 타이밍까지 함께 고려해야 하므로 디버깅이 복잡해질 수 있어요.
현재 컴포넌트에서는 데이터 fetching, 필터링 로직, 상태 업데이트, 사용자 이벤트 처리가 하나의 함수 안에 모여 있어요. 이처럼 책임이 분리되지 않으면, 특정 결과(`filteredNumbers`)가 잘못되었을 때 어떤 로직에서 문제가 발생했는지 파악하기가 어려워요. 특히 여러 상태 변화가 엮여 있을 경우, 상태 간의 상호작용이나 렌더 타이밍까지 함께 고려해야 하므로 디버깅이 복잡해질 수 있어요.

```tsx
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";

function filterEvenNumbers(numbers) {
return numbers.filter((num) => {
return num % 2 === 1;
return num % 2 === 0;
});
}

Expand Down Expand Up @@ -73,28 +73,31 @@ export default ComplexComponent;

### 간단하게 줄여보기

위 코드를 아래 코드처럼 간단하게 줄여서 테스트해볼 수 있어요. 원래 컴포넌트의 상태 관리, 비동기 호출, UI 요소를 제거하고, 핵심 로직인 filterEvenNumbers 함수만 간단한 배열로 테스트할 수 있도록 최소한의 코드로 줄였어요.
위 코드를 아래 코드처럼 간단하게 줄여서 테스트해볼 수 있어요. 원래 컴포넌트의 상태 관리, 비동기 호출, UI 요소를 제거하고, 핵심 로직인 `filterEvenNumbers` 함수만 간단한 배열로 테스트할 수 있도록 최소한의 코드로 줄였어요.

이렇게 코드를 단순화하면 복잡한 상태 관리나 네트워크 요청 없이도 문제가 발생하는 조건만으로 에러를 재현할 수 있어요.
이런 방식으로 문제를 단순하게 재현하고, 원인을 찾은 다음 원래 코드에 다시 반영하면 돼요.

```tsx
import React, { useEffect } from "react";
import { useState, useEffect } from "react";

function filterEvenNumbers(numbers) {
return numbers.filter((num) => {
return num % 2 === 1;
return num % 2 === 0;
});
}

const SimpleComponent = () => {
function SimpleComponent() {
const [filteredNumbers, setFilteredNumbers] = useState([]);

useEffect(() => {
const testArray = [1, 2, 3, 4, 5, 6];
const result = filterEvenNumbers(testArray);
setFilteredNumbers(result);
}, []);

return <div>테스트 중...</div>;
};
return <div>filteredNumbers: {filteredNumbers.join(", ")}</div>;
}

export default SimpleComponent;
```
Expand Down
2 changes: 1 addition & 1 deletion fundamentals/debug/pages/reproduce/trace.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@

**이전 상태가 현재 동작에 영향을 줄 수 있어요**

- 버그가 발생한 화면만 보지 말고, **그 전에 어떤 상태였는지를 함께 살펴봐야 해요.** 예를들어, `A 화면에서 특정 항목을 선택한 후, B 화면으로 넘어왔을 때만 발생함`처럼, **이전 컨텍스트가 버그의 중요한 재현 조건이 되는 경우**가 많아요.
- 버그가 발생한 화면만 보지 말고, **그 전에 어떤 상태였는지를 함께 살펴봐야 해요.** 예를 들어, `A 화면에서 특정 항목을 선택한 후, B 화면으로 넘어왔을 때만 발생함`처럼, **이전 컨텍스트가 버그의 중요한 재현 조건이 되는 경우**가 많아요.