<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>타마</title>
    <link>https://jae-oan.tistory.com/</link>
    <description>https://github.com/Jaeoan</description>
    <language>ko</language>
    <pubDate>Sat, 23 May 2026 09:56:30 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Ta_m</managingEditor>
    <image>
      <title>타마</title>
      <url>https://tistory1.daumcdn.net/tistory/7601360/attach/5e680627b8e94d6fa1c0d53814ec46a8</url>
      <link>https://jae-oan.tistory.com</link>
    </image>
    <item>
      <title>오류코드 정리</title>
      <link>https://jae-oan.tistory.com/13</link>
      <description>&lt;h1&gt;백엔드 &amp;amp; 프론트엔드 오류 코드&amp;nbsp;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발자라면 한 번쯤 마주치는 그 빨간 에러 메시지들. 오늘은 HTTP 상태 코드별로 왜 발생하는지, 어떻게 해결하는지 정리해본다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버와 클라이언트가 통신하는 한, 에러는 피할 수 없다. 중요한 건 &lt;b&gt;에러가 났을 때 누구의 문제인지 빠르게 판단하고, 어떻게 해결할지 아는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 HTTP 상태 코드를 기준으로 백엔드 측 오류와 프론트엔드 측 오류를 나눠서 정리하고, 실제 코드 예시와 함께 해결 방법을 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 백엔드 측 오류 (4xx, 5xx)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  400 Bad Request &amp;mdash; 잘못된 요청&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔한 케이스는 클라이언트가 보낸 데이터에 문제가 있을 때다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필수 파라미터 누락&lt;/li&gt;
&lt;li&gt;데이터 타입 불일치 (예: Number를 받아야 하는데 String이 옴)&lt;/li&gt;
&lt;li&gt;JSON 형식 오류&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력값 검증 미들웨어 도입 (express-validator, Joi, Zod 등)&lt;/li&gt;
&lt;li&gt;DTO 단계에서 유효성 검사&lt;/li&gt;
&lt;li&gt;명확한 에러 메시지 반환 (&quot;email 필드가 누락되었습니다&quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// Node.js 예시
app.post('/users', (req, res) =&amp;gt; {
  const { email, password } = req.body;
  if (!email || !password) {
    return res.status(400).json({ 
      message: '필수 입력값이 누락되었습니다.' 
    });
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  401 Unauthorized &amp;mdash; 인증 실패&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT 토큰 만료&lt;/li&gt;
&lt;li&gt;토큰 누락&lt;/li&gt;
&lt;li&gt;잘못된 인증 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증 미들웨어로 토큰 검증&lt;/li&gt;
&lt;li&gt;Refresh Token으로 자동 재발급&lt;/li&gt;
&lt;li&gt;클라이언트에서 401 수신 시 로그인 페이지로 리다이렉트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 건, &lt;b&gt;401은 &quot;당신이 누군지 모르겠다&quot;는 의미&lt;/b&gt;라는 점이다. 로그인부터 다시 해야 한다는 신호.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  403 Forbidden &amp;mdash; 권한 없음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증은 됐지만 해당 리소스 접근 권한 부족&lt;/li&gt;
&lt;li&gt;일반 유저가 관리자 페이지 접근 시도&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;역할 기반 접근 제어(RBAC) 구현&lt;/li&gt;
&lt;li&gt;권한 체크 미들웨어 작성&lt;/li&gt;
&lt;li&gt;보안상 민감한 경우 403 대신 404로 응답 (리소스 존재 자체를 숨김)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIP&lt;/b&gt; &amp;mdash; 401과 403은 자주 헷갈린다. 한 줄로 정리하면 &quot;401은 너 누구야?&quot;, &quot;403은 너인 건 알겠는데 들어오면 안 돼&quot;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  404 Not Found &amp;mdash; 리소스 없음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;존재하지 않는 엔드포인트&lt;/li&gt;
&lt;li&gt;DB에 없는 데이터 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라우트 핸들러 마지막에 404 처리 추가&lt;/li&gt;
&lt;li&gt;DB 조회 결과 null 체크&lt;/li&gt;
&lt;li&gt;일관된 404 응답 형식 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;// 404 핸들러
app.use((req, res) =&amp;gt; {
  res.status(404).json({ message: '요청한 리소스를 찾을 수 없습니다.' });
});&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  409 Conflict &amp;mdash; 충돌&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이메일/아이디 중복 가입&lt;/li&gt;
&lt;li&gt;동시성 문제로 데이터 충돌&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB 유니크 제약조건 활용&lt;/li&gt;
&lt;li&gt;사전 중복 체크 로직 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  500 Internal Server Error &amp;mdash; 서버 내부 오류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NullPointerException, undefined 접근&lt;/li&gt;
&lt;li&gt;DB 쿼리 오류, 커넥션 끊김&lt;/li&gt;
&lt;li&gt;코드 버그&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로벌 예외 핸들러 구현&lt;/li&gt;
&lt;li&gt;try-catch로 비동기 에러 처리&lt;/li&gt;
&lt;li&gt;로깅 시스템 도입 (Winston, Morgan)&lt;/li&gt;
&lt;li&gt;모니터링 도구 연동 (Sentry, Datadog)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;// Express 글로벌 에러 핸들러
app.use((err, req, res, next) =&amp;gt; {
  console.error(err.stack);
  res.status(500).json({ 
    message: '서버 내부 오류가 발생했습니다.' 
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;500이 떴다고 무작정 서버 탓 하면 안 된다. &lt;b&gt;비즈니스 로직 예외는 500이 아닌 400대로 응답해야 한다.&lt;/b&gt; 그래야 프론트가 적절히 대처할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  503 Service Unavailable &amp;mdash; 서비스 이용 불가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 점검/재배포&lt;/li&gt;
&lt;li&gt;트래픽 과부하&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Retry-After&lt;/code&gt; 헤더로 재시도 시점 안내&lt;/li&gt;
&lt;li&gt;Auto Scaling 적용&lt;/li&gt;
&lt;li&gt;로드 밸런서로 부하 분산&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 프론트엔드 측 오류&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  CORS 정책 위반&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 악명 높은 에러. 거의 모든 프론트엔드 개발자가 한 번쯤 만난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 출처(origin)의 API 호출을 브라우저가 차단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법 (백엔드에서 처리)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;const cors = require('cors');
app.use(cors({ origin: 'http://localhost:3000' }));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS는 이 글에서 다 다루기엔 너무 커서 따로 정리할 예정이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  TypeError / ReferenceError&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;undefined/null 값에 접근&lt;/li&gt;
&lt;li&gt;정의되지 않은 변수 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;옵셔널 체이닝(&lt;code&gt;?.&lt;/code&gt;)과 nullish 병합(&lt;code&gt;??&lt;/code&gt;) 활용&lt;/li&gt;
&lt;li&gt;TypeScript 도입으로 타입 안전성 확보&lt;/li&gt;
&lt;li&gt;기본값 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;// 안전한 접근
const userName = user?.profile?.name ?? '익명';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한 줄 차이로 앱이 죽고 안 죽고가 결정된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Promise / async-await 에러&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생 원인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 호출 실패 시 처리 누락&lt;/li&gt;
&lt;li&gt;Unhandled Promise Rejection&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;try-catch 블록 사용&lt;/li&gt;
&lt;li&gt;상태 코드별 분기 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;async function fetchUser() {
  try {
    const res = await fetch('/api/users');
    if (!res.ok) {
      if (res.status === 401) {
        // 로그인 페이지로 이동
      } else if (res.status === 404) {
        // 없음 처리
      }
      throw new Error(`HTTP ${res.status}`);
    }
    return await res.json();
  } catch (error) {
    console.error('API 호출 실패:', error);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  React 렌더링 오류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Error Boundary 컴포넌트로 감싸기&lt;/li&gt;
&lt;li&gt;폴백 UI 제공&lt;/li&gt;
&lt;li&gt;컴포넌트 단위 에러 격리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 한 부분에서 에러가 나도 전체 앱이 죽지 않게 만드는 게 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 공통 핵심 전략&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 핸들링의 본질은 결국 &lt;b&gt;&quot;예측 가능하게 만들기&quot;&lt;/b&gt; 다. 아래 6가지는 어느 프로젝트에서나 통하는 원칙이다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;전략&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;글로벌 에러 핸들링&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;모든 에러를 한 곳에서 일관된 형식으로 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;명확한 에러 메시지&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;어떤 필드, 어떤 이유로 실패했는지 정확히 안내&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;로깅&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;모든 에러는 로그로 기록 (디버깅 필수)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용자 친화적 메시지&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;프론트에서는 기술 용어 대신 이해하기 쉽게 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;재시도 로직&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;5xx 에러는 일정 시간 후 재시도 (Exponential Backoff)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;모니터링&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Sentry 등으로 실시간 에러 추적&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 꼭 기억할 4가지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 내용을 한 줄씩 압축하면 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 4xx는 클라이언트 책임, 5xx는 서버 책임&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 봐야 할 곳이 다르다. 4xx가 떴는데 백엔드 코드만 보면 답이 안 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 비즈니스 로직 예외는 5xx가 아닌 4xx로&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;이미 가입된 이메일입니다&quot;는 서버가 망가진 게 아니라 사용자가 잘못한 거다. 400대로 응답하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 확실하지 않으면 4xx로&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5xx 남발하면 진짜 서버 문제가 발생했을 때 묻혀버린다. 모니터링이 망가지는 지름길.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 프론트와 백엔드는 에러 응답 포맷을 미리 합의해야 함&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;// 이런 식으로 통일해두면 프론트가 행복해진다
{
  &quot;success&quot;: false,
  &quot;code&quot;: &quot;INVALID_EMAIL&quot;,
  &quot;message&quot;: &quot;이메일 형식이 올바르지 않습니다.&quot;,
  &quot;field&quot;: &quot;email&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 코드를 잘 다루는 개발자는 디버깅 시간이 절반으로 준다. 그리고 더 중요한 건, 사용자에게 친절한 앱을 만들 수 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 가장 자주 만나는 그 녀석 &amp;mdash; &lt;b&gt;CORS&lt;/b&gt; 에 대해서만 깊게 파볼 예정이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;해당 포스팅은 멋쟁이사자처럼 AI 세션 발표 자료를 기반으로 작성되었습니다.&lt;/i&gt;&lt;/p&gt;</description>
      <category>Front_end</category>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/13</guid>
      <comments>https://jae-oan.tistory.com/13#entry13comment</comments>
      <pubDate>Wed, 13 May 2026 18:06:41 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript</title>
      <link>https://jae-oan.tistory.com/12</link>
      <description>&lt;h2&gt;1. TypeScript&lt;/h2&gt;
&lt;h3&gt;1.1. 정의&lt;/h3&gt;
&lt;p&gt;TypeScript는 JavaScript를 확장한 정적 타입 언어다. JavaScript의 모든 문법을 포함하면서, 변수·매개변수·반환값에 타입을 명시할 수 있는 기능을 추가한다. 작성된 TypeScript 코드는 컴파일러를 거쳐 JavaScript로 변환되며, 변환된 코드는 브라우저 또는 Node.js 환경에서 실행된다.&lt;/p&gt;
&lt;h3&gt;1.2. 왜 JavaScript에 타입이 필요한가&lt;/h3&gt;
&lt;p&gt;JavaScript는 변수에 어떤 타입의 값이든 자유롭게 담을 수 있다. 이는 내용물을 표시하지 않은 상자에 비유할 수 있다. 상자 안의 값이 무엇인지 확인하려면 코드를 직접 실행해보거나 문서를 일일이 확인해야 한다. 프로젝트 규모가 커질수록 이 비용은 기하급수적으로 증가한다.&lt;/p&gt;
&lt;p&gt;TypeScript는 상자 외부에 “이 상자에는 숫자만 들어간다”, “이 상자에는 사용자 정보만 들어간다”와 같은 라벨을 붙이는 역할을 한다. 코드를 읽는 사람과 컴파일러 모두가 라벨을 통해 내용물을 미리 파악할 수 있다.&lt;/p&gt;
&lt;h3&gt;1.3. 설치&lt;/h3&gt;
&lt;p&gt;TypeScript 컴파일러는 npm 또는 yarn을 통해 전역으로 설치한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install -g typescript
# 또는
yarn global add typescript&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;-g&lt;/code&gt; 옵션은 패키지를 시스템 전역에 설치하며, 설치 이후에는 어떤 디렉터리에서도 &lt;code&gt;tsc&lt;/code&gt; 명령어를 사용할 수 있다.&lt;/p&gt;
&lt;h3&gt;1.4. 코드 비교&lt;/h3&gt;
&lt;p&gt;JavaScript와 TypeScript의 차이는 변수 선언부에서 가장 명확하게 드러난다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// JavaScript — 타입을 명시하지 않는다
let name = &amp;#39;홍길동&amp;#39;;
let age = 30;

// TypeScript — 타입을 명시한다
let userName: string = &amp;#39;홍길동&amp;#39;;
let userAge: number = 30;

// 복합 타입 — 인터페이스로 객체 구조를 정의한다
interface User {
  name: string;
  age: number;
  isStudent: boolean;
}

let student: User = {
  name: &amp;#39;김철수&amp;#39;,
  age: 25,
  isStudent: true
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;TypeScript는 &lt;code&gt;: 타입&lt;/code&gt; 형식의 &lt;strong&gt;타입 어노테이션(Type Annotation)&lt;/strong&gt; 을 통해 변수가 가질 수 있는 값의 형태를 제한한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. TypeScript와 JavaScript의 차이&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;JavaScript&lt;/th&gt;
&lt;th&gt;TypeScript&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;타입 시스템&lt;/td&gt;
&lt;td&gt;동적 타입&lt;/td&gt;
&lt;td&gt;정적 타입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;타입 검사 시점&lt;/td&gt;
&lt;td&gt;런타임&lt;/td&gt;
&lt;td&gt;컴파일 타임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;오류 발견 시점&lt;/td&gt;
&lt;td&gt;코드 실행 중&lt;/td&gt;
&lt;td&gt;코드 작성 중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;적합한 프로젝트 규모&lt;/td&gt;
&lt;td&gt;소규모&lt;/td&gt;
&lt;td&gt;대·중규모&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;2.1. 동적 타입과 정적 타입&lt;/h3&gt;
&lt;p&gt;JavaScript는 런타임에 변수의 타입이 결정되며, 동일한 변수에 다른 타입의 값을 자유롭게 재할당할 수 있다. TypeScript는 컴파일 시점에 타입이 결정되며, 선언된 타입과 다른 값을 할당하면 컴파일 오류가 발생한다.&lt;/p&gt;
&lt;p&gt;다음 두 코드는 동일한 동작을 시도하지만 결과가 다르다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// JavaScript — 문제없이 실행된다
let x = 10;
x = &amp;quot;안녕&amp;quot;;       // 정상
x = true;         // 정상&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// TypeScript — 컴파일 단계에서 차단된다
let x: number = 10;
x = &amp;quot;안녕&amp;quot;;       // 오류: &amp;#39;string&amp;#39; 형식은 &amp;#39;number&amp;#39; 형식에 할당할 수 없다.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;JavaScript는 이런 코드도 실행은 되지만, 이후 &lt;code&gt;x&lt;/code&gt;를 숫자로 가정하고 작성한 다른 코드에서 예기치 못한 버그가 발생한다. TypeScript는 이러한 버그가 만들어지는 순간 자체를 차단한다.&lt;/p&gt;
&lt;h3&gt;2.2. 장점과 단점&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;코드 가독성과 유지보수성이 향상된다.&lt;/li&gt;
&lt;li&gt;런타임 이전에 오류를 발견할 수 있어 디버깅 비용이 감소한다.&lt;/li&gt;
&lt;li&gt;IDE의 자동 완성, 타입 추론, 리팩토링 도구 지원이 강화된다.&lt;/li&gt;
&lt;li&gt;대규모 협업 환경에서 인터페이스 정의를 통해 의사소통 비용을 줄일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;단점&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;학습 곡선이 존재한다.&lt;/li&gt;
&lt;li&gt;컴파일 단계가 추가되어 빌드 시간이 증가한다.&lt;/li&gt;
&lt;li&gt;작은 규모의 프로젝트에서는 오버헤드가 될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;소규모 프로토타입이나 일회성 스크립트는 JavaScript로 충분하다. 그러나 여러 명이 협업하거나 장기적으로 유지보수해야 하는 프로젝트라면 TypeScript의 비용은 충분히 감수할 가치가 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. 컴파일 과정&lt;/h2&gt;
&lt;p&gt;JavaScript는 별도의 변환 과정 없이 브라우저나 Node.js에서 곧바로 실행된다. 반면 TypeScript는 브라우저나 Node.js가 직접 실행할 수 없으며, JavaScript로 변환하는 과정이 필요하다. 이 변환 과정을 &lt;strong&gt;컴파일(Compile)&lt;/strong&gt; 이라 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[1] .ts 파일 작성
        ↓
[2] tsc(TypeScript 컴파일러)가 타입 검사 수행
        ↓
[3] 타입 정보를 제거한 .js 파일 생성
        ↓
[4] 브라우저 또는 Node.js에서 실행&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;타입 정보는 컴파일 결과물에 포함되지 않는다. 즉, 타입은 개발 단계에서만 작동하는 안전장치이며, 실행 환경에는 영향을 주지 않는다. 이 때문에 TypeScript의 모든 타입 검사는 코드를 실행하기 전에 끝나야 의미가 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. 데이터 타입&lt;/h2&gt;
&lt;h3&gt;4.1. 기본 타입&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;타입&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;정수 및 실수를 포함한 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;문자열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt; 또는 &lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;의도적으로 비어 있음을 나타내는 값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;값이 할당되지 않은 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;any&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;모든 타입을 허용하는 타입&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;JavaScript에도 동일한 값들이 존재하지만, 변수에 타입을 강제할 방법이 없다. TypeScript는 위의 타입을 변수 선언 시점에 명시하여 잘못된 값의 할당을 사전에 차단한다.&lt;/p&gt;
&lt;h3&gt;4.2. any 타입에 관한 주의&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;any&lt;/code&gt; 타입은 모든 값을 허용하므로, 사실상 JavaScript 변수와 동일하게 동작한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let value: any = 10;
value = &amp;quot;문자열&amp;quot;;   // 오류 없음
value = true;       // 오류 없음&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;any&lt;/code&gt;를 남용하면 TypeScript가 제공하는 정적 분석의 이점을 잃게 되므로, 외부 라이브러리나 동적 데이터를 다루는 등 불가피한 경우에만 제한적으로 사용한다.&lt;/p&gt;
&lt;h3&gt;4.3. 변수 선언&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;let message: string = &amp;#39;Hello World!&amp;#39;;
const pi: number = 3.14159;
var age: number = 25;&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;키워드&lt;/th&gt;
&lt;th&gt;재할당&lt;/th&gt;
&lt;th&gt;스코프&lt;/th&gt;
&lt;th&gt;권장&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;const&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;불가&lt;/td&gt;
&lt;td&gt;블록&lt;/td&gt;
&lt;td&gt;우선 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;let&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;블록&lt;/td&gt;
&lt;td&gt;재할당이 필요한 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;var&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;함수&lt;/td&gt;
&lt;td&gt;사용을 지양한다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;var&lt;/code&gt;는 호이스팅과 함수 스코프로 인해 예측하기 어려운 동작을 유발할 수 있으므로, 현대 TypeScript/JavaScript에서는 &lt;code&gt;const&lt;/code&gt;와 &lt;code&gt;let&lt;/code&gt;만 사용하는 것이 표준이다. 기본은 &lt;code&gt;const&lt;/code&gt;로 선언하고, 값을 재할당해야 하는 경우에 한해 &lt;code&gt;let&lt;/code&gt;으로 변경하는 방식이 권장된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. 함수&lt;/h2&gt;
&lt;h3&gt;5.1. 기본 함수&lt;/h3&gt;
&lt;p&gt;함수는 JavaScript에도 존재하지만, TypeScript에서는 매개변수와 반환값 모두에 타입을 명시한다. 두 코드를 비교하면 차이가 분명하게 드러난다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// JavaScript — 어떤 값이든 받을 수 있다
function greet(name) {
  return `Hello, ${name}!`;
}
greet(123);   // 실행됨: &amp;quot;Hello, 123!&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// TypeScript — 문자열만 허용한다
function greet(name: string): string {
  return `Hello, ${name}!`;
}
greet(123);   // 오류: &amp;#39;number&amp;#39; 형식의 인수는 &amp;#39;string&amp;#39; 형식의 매개변수에 할당될 수 없다.

console.log(greet(&amp;#39;Jane&amp;#39;));&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;name: string&lt;/code&gt;은 매개변수의 타입이며, 함수 시그니처 끝의 &lt;code&gt;: string&lt;/code&gt;은 반환값의 타입이다. 반환 타입을 생략하면 컴파일러가 추론하지만, 명시적으로 작성하는 편이 가독성에 유리하다.&lt;/p&gt;
&lt;h3&gt;5.2. 옵셔널 파라미터&lt;/h3&gt;
&lt;p&gt;매개변수 이름 뒤에 &lt;code&gt;?&lt;/code&gt;를 붙이면 해당 매개변수는 선택적이 된다. 호출 시 값을 전달하지 않아도 오류가 발생하지 않으며, 전달되지 않은 경우 값은 &lt;code&gt;undefined&lt;/code&gt;가 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function testOptionalParam(base: string, optionalParam?: string) {
  console.log(&amp;#39;base: &amp;#39;, base);
  console.log(&amp;#39;optionalParam: &amp;#39;, optionalParam);
}

testOptionalParam(&amp;#39;필수값&amp;#39;);
testOptionalParam(&amp;#39;필수값&amp;#39;, &amp;#39;추가값&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;옵셔널 파라미터는 반드시 필수 파라미터 뒤에 위치해야 한다. JavaScript에서는 매개변수의 필수 여부를 문법적으로 구분할 수 없고 함수 내부에서 직접 검사해야 했지만, TypeScript는 이를 시그니처 단계에서 명시한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. 인터페이스&lt;/h2&gt;
&lt;h3&gt;6.1. 정의&lt;/h3&gt;
&lt;p&gt;인터페이스(Interface)는 객체가 가져야 할 속성과 그 타입을 정의하는 일종의 명세다. 객체의 구조를 사전에 약속함으로써, 잘못된 형태의 객체가 사용되는 것을 컴파일 단계에서 차단한다. 건축에 비유하면 인터페이스는 설계도이며, 객체는 그 설계도를 따라 지어진 건물이다.&lt;/p&gt;
&lt;p&gt;JavaScript에는 인터페이스 문법이 없다. 객체의 형태가 코드 어디에서도 보장되지 않으므로, 잘못된 속성에 접근하거나 오타를 내도 런타임이 되어서야 문제가 드러난다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface User {
  name: string;
  age: number;
}

const user: User = { name: &amp;#39;John&amp;#39;, age: 30 };&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;User&lt;/code&gt; 인터페이스를 따르지 않는 객체를 할당하면 컴파일 오류가 발생한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const invalidUser: User = { name: &amp;#39;John&amp;#39; };
// 오류: &amp;#39;age&amp;#39; 속성이 &amp;#39;{ name: string; }&amp;#39; 형식에 없지만 &amp;#39;User&amp;#39; 형식에서 필수다.&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;6.2. 옵셔널 속성&lt;/h3&gt;
&lt;p&gt;인터페이스의 속성에도 &lt;code&gt;?&lt;/code&gt;를 사용하여 선택적 속성을 정의할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface User {
  name: string;
  age?: number;
}

const user1: User = { name: &amp;#39;John&amp;#39; };
const user2: User = { name: &amp;#39;Jane&amp;#39;, age: 30 };&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;user1&lt;/code&gt;은 &lt;code&gt;age&lt;/code&gt; 속성이 없지만 &lt;code&gt;User&lt;/code&gt; 타입을 만족하며, &lt;code&gt;user2&lt;/code&gt;는 &lt;code&gt;age&lt;/code&gt; 속성을 포함하므로 마찬가지로 유효하다. 옵셔널 속성은 값이 존재하지 않거나 &lt;code&gt;undefined&lt;/code&gt;인 경우를 허용한다. 회원가입 시 이름은 필수이지만 나이는 선택 입력으로 받는 경우가 대표적인 사용 예다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. tsconfig.json&lt;/h2&gt;
&lt;h3&gt;7.1. 역할&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;tsconfig.json&lt;/code&gt;은 TypeScript 프로젝트의 컴파일러 설정 파일이다. 이 파일이 위치한 디렉터리가 프로젝트의 루트로 인식되며, 컴파일 대상·출력 위치·언어 버전 등 컴파일 동작 전반이 이 파일을 통해 제어된다. JavaScript에는 컴파일 단계가 없으므로 이런 설정 파일이 필요하지 않다.&lt;/p&gt;
&lt;h3&gt;7.2. 프로젝트 초기화 절차&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 1. TypeScript 전역 설치
npm install -g typescript

# 2. tsconfig.json 생성
tsc --init

# 3. .ts 파일 작성

# 4. 컴파일 실행
tsc&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;tsc --init&lt;/code&gt;은 기본 설정이 포함된 &lt;code&gt;tsconfig.json&lt;/code&gt;을 자동으로 생성한다.&lt;/p&gt;
&lt;h3&gt;7.3. 주요 옵션&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;옵션&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;outDir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;컴파일된 JavaScript 파일이 저장될 출력 디렉터리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;include&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;컴파일에 포함할 파일 또는 디렉터리 패턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exclude&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;컴파일에서 제외할 파일 또는 디렉터리 패턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allowJs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JavaScript 파일(.js)의 컴파일 허용 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;7.4. 설정 예시&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;compilerOptions&amp;quot;: {
    &amp;quot;outDir&amp;quot;: &amp;quot;./dist&amp;quot;,
    &amp;quot;target&amp;quot;: &amp;quot;ES6&amp;quot;,
    &amp;quot;allowJs&amp;quot;: true
  },
  &amp;quot;include&amp;quot;: [&amp;quot;src/**/*&amp;quot;],
  &amp;quot;exclude&amp;quot;: [&amp;quot;node_modules&amp;quot;]
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 설정은 &lt;code&gt;src&lt;/code&gt; 디렉터리 하위의 모든 파일을 ES6 문법의 JavaScript로 컴파일하여 &lt;code&gt;dist&lt;/code&gt; 디렉터리에 출력하며, &lt;code&gt;node_modules&lt;/code&gt;는 컴파일 대상에서 제외한다.&lt;/p&gt;
&lt;h2&gt;TypeScript가 필요한 이유 — 코드 예시&lt;/h2&gt;
&lt;h3&gt;예시 1. 한 달 뒤의 나, 또는 다른 동료가 함수를 사용할 때&lt;/h3&gt;
&lt;p&gt;JavaScript 함수는 시그니처만으로 사용법을 알 수 없다. 매개변수의 의미와 타입, 반환값의 형태를 모두 함수 본문이나 별도의 주석에 의존해야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// JavaScript — 한 달 뒤에 다시 보면?
function calculateDiscount(item, rate, options) {
  // rate는 0~1 사이의 비율인가, 0~100 사이의 퍼센트인가?
  // options에는 어떤 속성이 들어가야 하는가?
  // 반환값은 할인된 가격인가, 할인 금액인가?
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScript로 작성된 같은 함수는 시그니처가 곧 사용 설명서가 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// TypeScript — 시그니처만 봐도 사용법이 명확하다
interface Product {
  id: string;
  price: number;
}

interface DiscountOptions {
  applyToShipping: boolean;
  maxAmount?: number;
}

function calculateDiscount(
  item: Product,
  rate: number,
  options: DiscountOptions
): number {
  return item.price * (1 - rate);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;함수를 호출하는 쪽에서는 IDE가 매개변수의 타입과 옵션의 구조를 자동으로 안내한다. 작성자에게 직접 물어볼 필요가 없으며, 작성자가 회사를 떠난 뒤에도 코드 자체가 문서 역할을 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;예시 2. 객체 형태가 보장되지 않을 때 발생하는 오류&lt;/h3&gt;
&lt;p&gt;서버에서 받아온 응답이나 외부 모듈에서 전달된 객체를 다룰 때, JavaScript는 객체의 구조를 보장하지 않는다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// JavaScript — 오타를 내도 실행은 된다
function renderUser(user) {
  console.log(user.nme); // 오타: name → nme
}

renderUser({ name: &amp;#39;홍길동&amp;#39;, age: 30 });
// 출력: undefined
// 화면에는 빈 값이 그대로 표시된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScript는 이런 실수를 컴파일 시점에 잡아낸다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;interface User {
  name: string;
  age: number;
}

function renderUser(user: User) {
  console.log(user.nme);
  // 오류: &amp;#39;nme&amp;#39; 속성이 &amp;#39;User&amp;#39; 형식에 없습니다. &amp;#39;name&amp;#39;을(를) 사용하시겠습니까?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;코드 작성자가 5명, 10명으로 늘어나도 인터페이스만 공유하면 모두가 동일한 객체 구조를 전제로 작업할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;예시 3. 변경 영향 범위가 자동으로 추적된다&lt;/h3&gt;
&lt;p&gt;기존 함수의 시그니처를 수정하는 상황을 가정해보자. 매개변수 하나를 추가하거나 반환 타입을 바꾸는 작업이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// JavaScript — 함수 시그니처 변경 후
function getUser(id) {                      // 기존
  return { name: &amp;#39;홍길동&amp;#39; };
}

function getUser(id, includeEmail) {        // 변경: 매개변수 추가
  return { name: &amp;#39;홍길동&amp;#39;, email: &amp;#39;...&amp;#39; };
}

// 어디서 이 함수를 쓰고 있었는지 일일이 검색해야 한다.
// 빠뜨린 호출 지점은 런타임에 가서야 오류로 드러난다.&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// TypeScript — 모든 호출 지점이 즉시 오류로 표시된다
function getUser(id: string, includeEmail: boolean): User {
  // ...
}

getUser(&amp;#39;user-1&amp;#39;);
// 오류: 2개의 인수가 필요한데 1개를 가져왔습니다.

getUser(&amp;#39;user-2&amp;#39;, true);  // ✅ OK&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;컴파일러가 변경의 파급 범위를 즉시 알려주므로, 수정해야 할 위치가 한눈에 드러난다. 이 차이는 코드베이스가 클수록, 변경이 잦을수록 더 크게 벌어진다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;세 예시 모두 한 사람이 한 번 작성하고 끝나는 코드가 아니라, 여러 사람이 오랜 기간 함께 다듬는 코드일수록 효과가 크다. 결국 TypeScript는 협업과 유지보수를 위한 도구다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;TypeScript는 JavaScript의 동적 타입 특성에서 비롯되는 런타임 오류를 컴파일 단계에서 차단하기 위해 설계된 언어다. 타입 어노테이션·인터페이스·컴파일러 옵션은 모두 코드의 안정성과 유지보수성을 높이기 위한 도구이며, 개별 문법보다 정적 타입 시스템이라는 큰 그림을 이해하는 것이 학습의 핵심이다. JavaScript와의 차이를 비교하면서 코드를 작성해보면, 타입이 단순히 문법적 제약이 아니라 협업과 유지보수를 위한 설계 도구임을 체감할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Tags&lt;/strong&gt;: &lt;code&gt;#TypeScript&lt;/code&gt; &lt;code&gt;#JavaScript&lt;/code&gt; &lt;code&gt;#NodeJS&lt;/code&gt; &lt;code&gt;#정적타입&lt;/code&gt; &lt;code&gt;#개발입문&lt;/code&gt;&lt;/p&gt;</description>
      <category>Front_end</category>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/12</guid>
      <comments>https://jae-oan.tistory.com/12#entry12comment</comments>
      <pubDate>Wed, 6 May 2026 15:12:55 +0900</pubDate>
    </item>
    <item>
      <title>리액트  이벤트, State, Ref, Effect 정리</title>
      <link>https://jae-oan.tistory.com/11</link>
      <description>&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트를 처음 배우면 이런 생각이 많이 든다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버튼을 누르면 화면이 왜 바뀌지?&lt;/li&gt;
&lt;li&gt;입력창에 글자를 치면 그 값은 어디에 저장되지?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;state&lt;/code&gt;랑 &lt;code&gt;ref&lt;/code&gt;는 뭐가 다른 거지?&lt;/li&gt;
&lt;li&gt;DOM을 직접 만지면 안 된다는데, 그럼 언제 &lt;code&gt;ref&lt;/code&gt;를 써야 하지?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useEffect&lt;/code&gt;는 왜 필요한 거지?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 이 흐름을 &lt;b&gt;이벤트 &amp;rarr; State &amp;rarr; Ref &amp;rarr; Effect&lt;/b&gt; 순서로 아주 쉽게 정리해보려고 한다.&lt;br /&gt;이 순서대로 이해하면 리액트가 왜 그렇게 동작하는지 훨씬 잘 보인다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이벤트란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트는 &lt;b&gt;사용자와의 상호작용으로 일어난 사건&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 것들이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버튼 클릭&lt;/li&gt;
&lt;li&gt;입력 값 변경&lt;/li&gt;
&lt;li&gt;마우스 호버&lt;/li&gt;
&lt;li&gt;인풋 포커스&lt;/li&gt;
&lt;li&gt;창 크기 조절&lt;/li&gt;
&lt;li&gt;동영상 재생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 사용자가 화면에서 어떤 행동을 했을 때 발생하는 것이 이벤트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면&lt;br /&gt;&lt;b&gt;&amp;ldquo;사용자가 뭔가 했다&amp;rdquo; = 이벤트가 발생했다&lt;/b&gt;&lt;br /&gt;이렇게 이해하면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이벤트 핸들러란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 핸들러는 &lt;b&gt;이벤트가 발생했을 때 실행되는 함수&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버튼을 눌렀을 때 숫자를 증가시키기&lt;/li&gt;
&lt;li&gt;입력값이 바뀌었을 때 state에 저장하기&lt;/li&gt;
&lt;li&gt;클릭했을 때 알림창 띄우기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 동작들이 모두 이벤트 핸들러 안에서 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄로 정리하면 이렇게 볼 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;이벤트&lt;/b&gt; = 사건&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트 핸들러&lt;/b&gt; = 그 사건이 일어났을 때 실행되는 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 코드는 아래처럼 생긴다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function App() {
  function handleClick() {
    alert(&quot;버튼이 클릭되었습니다!&quot;);
  }

  return &amp;lt;button onClick={handleClick}&amp;gt;클릭&amp;lt;/button&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;onClick&lt;/code&gt;은 클릭 이벤트를 연결하는 부분이고, &lt;code&gt;handleClick&lt;/code&gt;은 실제로 실행되는 함수다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리액트에서 이벤트를 처리하는 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 이벤트를 처리할 때는 보통 이런 순서로 진행된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 이벤트 핸들러 선언&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 함수를 만든다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 이벤트 핸들러 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 함수 안에 실행할 동작을 적는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 이벤트에 이벤트 핸들러 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼이나 input 같은 요소에 연결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 흐름은 결국 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수 만들기 &amp;rarr; 동작 작성 &amp;rarr; 요소에 연결&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 어려워 보여도 실제로는 이 패턴이 계속 반복된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이벤트 전파란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트는 한 요소에서만 끝나는 것이 아니라 다른 요소로 전달될 수 있다.&lt;br /&gt;이걸 &lt;b&gt;이벤트 전파&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 전파는 보통 세 단계로 생각할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 캡처링 단계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트가 상위 요소에서 하위 요소로 내려가는 단계&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 타깃 단계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트가 실제로 발생한 요소에 도달하는 단계&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 버블링 단계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트가 다시 하위 요소에서 상위 요소로 올라가는 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자 입장에서는 일단 &lt;b&gt;버블링&lt;/b&gt;을 먼저 이해하는 게 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 구조가 있다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;div onClick={() =&amp;gt; console.log(&quot;부모 클릭&quot;)}&amp;gt;
  &amp;lt;button onClick={() =&amp;gt; console.log(&quot;자식 클릭&quot;)}&amp;gt;버튼&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 버튼을 클릭하면 보통&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;자식 버튼 클릭&lt;/li&gt;
&lt;li&gt;부모 div 클릭&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 둘 다 실행될 수 있다.&lt;br /&gt;이게 바로 버블링 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모까지 이벤트가 전달되지 않게 막고 싶다면 &lt;code&gt;stopPropagation()&lt;/code&gt;을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function App() {
  function handleParentClick() {
    console.log(&quot;부모 클릭&quot;);
  }

  function handleChildClick(e) {
    e.stopPropagation();
    console.log(&quot;자식 클릭&quot;);
  }

  return (
    &amp;lt;div onClick={handleParentClick}&amp;gt;
      &amp;lt;button onClick={handleChildClick}&amp;gt;클릭&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;State란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;State는 &lt;b&gt;컴포넌트가 기억해야 할 것을 저장하는 공간&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서는 화면이 바뀌는 이유가 결국 state가 바뀌기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 값들이 state가 될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 카운터 숫자&lt;/li&gt;
&lt;li&gt;input에 입력한 값&lt;/li&gt;
&lt;li&gt;모달이 열려 있는지 여부&lt;/li&gt;
&lt;li&gt;체크박스 선택 상태&lt;/li&gt;
&lt;li&gt;할 일 목록 데이터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;br /&gt;&lt;b&gt;화면을 바꾸는 데 필요한 데이터는 state에 저장한다&lt;/b&gt;&lt;br /&gt;이렇게 이해하면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 state가 바뀌면 화면도 바뀔까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 화면을 직접 하나하나 수정하는 방식보다,&lt;br /&gt;&lt;b&gt;현재 데이터에 맞는 화면을 다시 계산하는 방식&lt;/b&gt;으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 count가 0이면 화면에 0이 보이고,&lt;br /&gt;버튼을 눌러 count가 1이 되면 화면도 1로 다시 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 핵심 흐름은 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 발생 &amp;rarr; 함수 실행 &amp;rarr; state 변경 &amp;rarr; 화면 변경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 리액트의 가장 중요한 동작 흐름이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Props와 State 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트를 공부하다 보면 props와 state를 자주 비교하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Props&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부모 컴포넌트가 자식 컴포넌트에게 전달하는 데이터&lt;/li&gt;
&lt;li&gt;바깥에서 들어오는 값&lt;/li&gt;
&lt;li&gt;자식 입장에서는 보통 읽기 전용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;State&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트 내부에서 직접 관리하는 데이터&lt;/li&gt;
&lt;li&gt;사용자 행동에 따라 바뀔 수 있는 값&lt;/li&gt;
&lt;li&gt;값이 바뀌면 화면도 바뀜&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 구분하면 이렇게 생각하면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;밖에서 받은 값&lt;/b&gt; &amp;rarr; props&lt;/li&gt;
&lt;li&gt;&lt;b&gt;내가 직접 관리하면서 바꾸는 값&lt;/b&gt; &amp;rarr; state&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;카운터 앱으로 State 이해하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;State를 가장 쉽게 이해할 수 있는 대표 예제가 카운터 앱이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 누르면 숫자가 증가하거나 감소한다.&lt;br /&gt;이건 단순해 보이지만 리액트 핵심 개념이 전부 들어 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

function App() {
  const [count, setCount] = useState(0);

  function increase() {
    setCount(count + 1);
  }

  function decrease() {
    setCount(count - 1);
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;카운터: {count}&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={decrease}&amp;gt;-&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={increase}&amp;gt;+&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서 중요한 부분은 아래 두 개다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;count&lt;/code&gt;: 현재 상태값&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setCount&lt;/code&gt;: 상태를 바꾸는 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 누르면 이벤트 핸들러가 실행되고,&lt;br /&gt;그 안에서 &lt;code&gt;setCount()&lt;/code&gt;가 호출되며,&lt;br /&gt;state가 바뀌고 화면이 다시 렌더링된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 카운터 앱은 리액트의 핵심 흐름을 가장 간단하게 보여주는 예제다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ref란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;code&gt;ref&lt;/code&gt;를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref는 &lt;b&gt;컴포넌트가 기억하고 싶은 정보를 저장하는 공간&lt;/b&gt;이다.&lt;br /&gt;하지만 state와 가장 큰 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로&lt;br /&gt;&lt;b&gt;값을 바꿔도 리렌더링이 일어나지 않는다&lt;/b&gt;는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref는 &lt;code&gt;useRef&lt;/code&gt;로 만들고, &lt;code&gt;current&lt;/code&gt; 속성을 통해 값을 읽거나 바꾼다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useRef } from &quot;react&quot;;

function App() {
  const countRef = useRef(0);

  function handleClick() {
    countRef.current += 1;
    console.log(countRef.current);
  }

  return &amp;lt;button onClick={handleClick}&amp;gt;클릭&amp;lt;/button&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 버튼을 누르면 &lt;code&gt;countRef.current&lt;/code&gt; 값은 증가한다.&lt;br /&gt;하지만 이건 state가 아니기 때문에 화면이 다시 렌더링되지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, ref는 이렇게 생각하면 편하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값은 기억하고 싶다&lt;/li&gt;
&lt;li&gt;그런데 화면은 다시 안 바뀌어도 된다&lt;/li&gt;
&lt;li&gt;그럴 때 ref를 사용한다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ref와 State 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘은 헷갈리기 쉽다.&lt;br /&gt;그래서 아래처럼 확실히 구분해두면 좋다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;State&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;useState&lt;/code&gt;로 선언&lt;/li&gt;
&lt;li&gt;setter 함수로 값 변경&lt;/li&gt;
&lt;li&gt;값이 바뀌면 리렌더링됨&lt;/li&gt;
&lt;li&gt;화면에 보이는 데이터를 관리할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Ref&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;useRef&lt;/code&gt;로 선언&lt;/li&gt;
&lt;li&gt;&lt;code&gt;current&lt;/code&gt;로 값 접근&lt;/li&gt;
&lt;li&gt;값이 바뀌어도 리렌더링되지 않음&lt;/li&gt;
&lt;li&gt;화면과 직접 관련 없는 값 저장이나 DOM 접근에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄 요약:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;화면을 바꾸려면 state, 화면 재렌더링 없이 값만 기억하려면 ref&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리액트에서 DOM을 직접 조작하지 않는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 UI를 &lt;b&gt;선언적으로 표현&lt;/b&gt;한다.&lt;br /&gt;즉, state를 바꾸면 리액트가 화면을 알아서 다시 업데이트한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 대부분의 경우는 DOM을 직접 조작할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 자바스크립트에서는 이런 식으로 많이 했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;document.querySelector()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;innerText&lt;/code&gt; 변경&lt;/li&gt;
&lt;li&gt;&lt;code&gt;style&lt;/code&gt; 직접 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 리액트에서는 보통 그렇게 하지 않는다.&lt;br /&gt;왜냐하면 state만 바꾸면 화면이 자동으로 다시 계산되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자 바꾸고 싶다 &amp;rarr; state 변경&lt;/li&gt;
&lt;li&gt;텍스트 바꾸고 싶다 &amp;rarr; state 변경&lt;/li&gt;
&lt;li&gt;UI 보이게/숨기고 싶다 &amp;rarr; state 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름이 기본이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그럼 언제 ref로 DOM을 직접 만질까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분은 state로 해결되지만, 어떤 경우에는 직접 DOM을 조작해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 경우는 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;input에 포커스를 주고 싶을 때&lt;/li&gt;
&lt;li&gt;특정 위치로 스크롤 이동시키고 싶을 때&lt;/li&gt;
&lt;li&gt;요소의 크기나 위치를 계산하고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 &lt;code&gt;ref&lt;/code&gt;를 사용해서 DOM 요소에 직접 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 input에 포커스를 주는 코드는 이렇게 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useRef } from &quot;react&quot;;

function App() {
  const inputRef = useRef(null);

  function focusInput() {
    inputRef.current.focus();
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input ref={inputRef} /&amp;gt;
      &amp;lt;button onClick={focusInput}&amp;gt;입력창 포커스&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;inputRef.current&lt;/code&gt;는 실제 input DOM 요소를 가리킨다.&lt;br /&gt;그래서 &lt;code&gt;focus()&lt;/code&gt; 같은 DOM 메서드를 직접 사용할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Effect란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;code&gt;useEffect&lt;/code&gt;를 볼 차례다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Effect는 &lt;b&gt;렌더링이 완료된 후 실행되는 후처리 작업&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면&lt;br /&gt;&lt;b&gt;화면이 그려진 뒤에 해야 하는 일&lt;/b&gt;&lt;br /&gt;이라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 예시는 이런 것들이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 호출&lt;/li&gt;
&lt;li&gt;이벤트 리스너 등록&lt;/li&gt;
&lt;li&gt;타이머 시작&lt;/li&gt;
&lt;li&gt;콘솔 출력&lt;/li&gt;
&lt;li&gt;구독 시작 및 해제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, JSX 안에서 화면을 그리는 것과는 다른 종류의 작업을 effect에서 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 count가 바뀔 때마다 콘솔에 출력하고 싶다면 이렇게 할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useState, useEffect } from &quot;react&quot;;

function App() {
  const [count, setCount] = useState(0);

  useEffect(() =&amp;gt; {
    console.log(`Count: ${count}`);
  }, [count]);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{count}&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;증가&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;[count]&lt;/code&gt;는 count가 바뀔 때 effect를 다시 실행하라는 뜻이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴포넌트는 무엇으로 구성될까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 컴포넌트는 크게 아래처럼 나눠서 생각할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 렌더링 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 무엇을 보여줄지 결정하는 부분&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 이벤트 핸들러&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 행동이 발생했을 때 실행되는 부분&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Effect&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링이 끝난 뒤 외부 환경과 상호작용하는 부분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나누면 코드 구조도 더 잘 보이고,&lt;br /&gt;&amp;ldquo;이건 화면 코드인지&amp;rdquo;, &amp;ldquo;이건 클릭 함수인지&amp;rdquo;, &amp;ldquo;이건 후처리인지&amp;rdquo; 구분하기 쉬워진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;라이프사이클이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 컴포넌트에도 라이프사이클이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 컴포넌트도&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생기고&lt;/li&gt;
&lt;li&gt;바뀌고&lt;/li&gt;
&lt;li&gt;사라진다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 아래 세 가지로 이해하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마운트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 처음 화면에 나타날 때&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;업데이트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;state나 props가 바뀌어서 다시 렌더링될 때&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;언마운트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 화면에서 사라질 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useEffect&lt;/code&gt;는 이런 라이프사이클 흐름과 함께 자주 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처음 마운트됐을 때 데이터 요청&lt;/li&gt;
&lt;li&gt;특정 값이 바뀔 때 다시 실행&lt;/li&gt;
&lt;li&gt;언마운트될 때 정리 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 사용할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;타이머 앱으로 Effect 이해하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Effect를 가장 쉽게 이해할 수 있는 예시 중 하나가 타이머 앱이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타이머 앱에서는 보통 이런 기능이 들어간다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시작 버튼을 누르면 시간이 줄어든다&lt;/li&gt;
&lt;li&gt;일시정지 버튼을 누르면 잠시 멈춘다&lt;/li&gt;
&lt;li&gt;다시 시작할 수 있다&lt;/li&gt;
&lt;li&gt;시간이 0이 되면 초기화한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 단순히 state만 쓰는 문제가 아니라,&lt;br /&gt;&lt;b&gt;시간이 흐르는 외부 동작&lt;/b&gt;을 함께 다뤄야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 effect가 잘 어울린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 타이머 개념을 아주 간단히 표현하면 이런 느낌이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useState, useEffect } from &quot;react&quot;;

function App() {
  const [time, setTime] = useState(10);
  const [isRunning, setIsRunning] = useState(false);

  useEffect(() =&amp;gt; {
    if (!isRunning) return;

    const timer = setInterval(() =&amp;gt; {
      setTime((prev) =&amp;gt; prev - 1);
    }, 1000);

    return () =&amp;gt; clearInterval(timer);
  }, [isRunning]);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{time}&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setIsRunning(true)}&amp;gt;시작&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setIsRunning(false)}&amp;gt;일시정지&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서 핵심은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;isRunning&lt;/code&gt;이 true일 때 타이머 시작&lt;/li&gt;
&lt;li&gt;effect 안에서 &lt;code&gt;setInterval()&lt;/code&gt; 실행&lt;/li&gt;
&lt;li&gt;정리 함수에서 &lt;code&gt;clearInterval()&lt;/code&gt; 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, effect는 단순한 화면 출력이 아니라&lt;br /&gt;&lt;b&gt;외부 동작을 관리하는 도구&lt;/b&gt;라고 이해하면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실무 감각으로 정리하면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트를 배우면서 가장 중요한 건&lt;br /&gt;&amp;ldquo;어떤 상황에 어떤 도구를 써야 하는지&amp;rdquo; 감을 잡는 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 행동을 감지할 때 사용&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 핸들러&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 행동이 발생했을 때 실행할 함수를 만들 때 사용&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;state&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 바뀌면 화면도 바뀌어야 할 때 사용&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ref&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값은 기억해야 하지만 화면을 다시 그릴 필요가 없을 때 사용&lt;br /&gt;또는 DOM 요소에 직접 접근해야 할 때 사용&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;effect&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링 이후 외부 환경과 상호작용해야 할 때 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초보자가 가장 많이 헷갈리는 포인트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. state와 ref를 자꾸 헷갈림&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기준은 딱 하나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;값이 바뀌었을 때 화면도 바뀌어야 하면 state&lt;/b&gt;&lt;br /&gt;&lt;b&gt;화면은 안 바뀌어도 되면 ref&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. DOM을 너무 빨리 직접 만지려고 함&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서는 대부분 state로 해결한다.&lt;br /&gt;정말 필요한 경우에만 ref로 DOM에 접근한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. useEffect를 아무 데나 쓰려고 함&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;effect는 &amp;ldquo;렌더링 후 해야 하는 작업&amp;rdquo;에 쓰는 것이다.&lt;br /&gt;단순 계산이나 일반 함수 실행은 굳이 effect에 넣지 않아도 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;한 번에 흐름 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 내용을 한 줄 흐름으로 연결하면 이렇게 볼 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 사용자가 행동한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: 버튼 클릭, 입력, 마우스 이동&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 이벤트가 발생한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트가 그 행동을 감지한다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 이벤트 핸들러가 실행된다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록해둔 함수가 실행된다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 필요한 값이 바뀐다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화면에 반영해야 하면 state&lt;/li&gt;
&lt;li&gt;화면에 반영할 필요 없으면 ref&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 화면이 다시 렌더링된다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;state가 바뀐 경우 UI가 업데이트된다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 렌더링 후 effect가 실행된다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하면 API 호출, 타이머, 이벤트 리스너 같은 외부 작업을 처리한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름이 보이면 리액트 공부가 훨씬 쉬워진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트를 처음 배울 때는 용어가 많아서 어렵게 느껴진다.&lt;br /&gt;하지만 핵심은 생각보다 단순하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이벤트는 사용자의 행동이다&lt;/li&gt;
&lt;li&gt;이벤트 핸들러는 그 행동에 반응하는 함수다&lt;/li&gt;
&lt;li&gt;state는 화면을 바꾸는 값이다&lt;/li&gt;
&lt;li&gt;ref는 화면 렌더링 없이 기억하거나 DOM에 접근할 때 쓴다&lt;/li&gt;
&lt;li&gt;effect는 렌더링이 끝난 뒤 외부 작업을 처리할 때 쓴다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 리액트의 핵심 흐름은 결국 이것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 발생 &amp;rarr; 함수 실행 &amp;rarr; state/ref 변경 &amp;rarr; 필요 시 화면 갱신 &amp;rarr; effect 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름을 제대로 이해하면&lt;br /&gt;카운터 앱, 투두리스트, 마우스 트래커, 타이머 앱 같은 예제들이 왜 그렇게 만들어지는지도 자연스럽게 보이기 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트를 공부하는 초보자라면 오늘은 딱 이 한 문장만 기억해도 좋다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 화면을 직접 고치는 방식이 아니라, 상태를 바꿔서 화면을 다시 그리는 방식으로 동작한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;같이 보면 좋은 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React event&lt;/li&gt;
&lt;li&gt;event handler&lt;/li&gt;
&lt;li&gt;state&lt;/li&gt;
&lt;li&gt;props&lt;/li&gt;
&lt;li&gt;ref&lt;/li&gt;
&lt;li&gt;useRef&lt;/li&gt;
&lt;li&gt;DOM 조작&lt;/li&gt;
&lt;li&gt;useEffect&lt;/li&gt;
&lt;li&gt;라이프사이클&lt;/li&gt;
&lt;li&gt;타이머 앱&lt;/li&gt;
&lt;li&gt;카운터 앱&lt;/li&gt;
&lt;li&gt;투두리스트&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/11</guid>
      <comments>https://jae-oan.tistory.com/11#entry11comment</comments>
      <pubDate>Wed, 8 Apr 2026 00:39:47 +0900</pubDate>
    </item>
    <item>
      <title>강의 자료 2</title>
      <link>https://jae-oan.tistory.com/8</link>
      <description>&lt;h1&gt;React 입문 — JSX · 컴포넌트 · CSS 스타일링&lt;/h1&gt;
&lt;p&gt;이 글은 프론트엔드 개발 커리큘럼 Week 1 강의 내용을 정리한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;React란 무엇인가?&lt;/h2&gt;
&lt;p&gt;React는 Meta(구 Facebook)가 만든 &lt;strong&gt;UI 라이브러리&lt;/strong&gt;다. 웹 페이지의 화면(UI)을 만드는 데 특화되어 있으며, 핵심 특징은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;컴포넌트 기반&lt;/strong&gt; — UI를 독립적인 조각으로 나눠 관리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Virtual DOM&lt;/strong&gt; — 변경된 부분만 업데이트해서 성능이 뛰어남&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;재사용성&lt;/strong&gt; — 한 번 만든 컴포넌트를 어디서든 재사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Node.js 설치하기&lt;/h2&gt;
&lt;p&gt;React 개발을 시작하려면 &lt;strong&gt;Node.js&lt;/strong&gt;가 필요하다. &lt;a href=&quot;https://nodejs.org&quot;&gt;nodejs.org&lt;/a&gt;에서 LTS 버전을 설치한 뒤 아래 명령어로 확인하자.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;node -v
npm -v&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;버전 번호가 출력되면 설치 완료.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;첫 리액트 프로젝트 만들기 (Vite)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Vite&lt;/strong&gt;를 사용하면 빠르게 React 프로젝트를 시작할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run dev&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;브라우저에서 &lt;code&gt;http://localhost:5173&lt;/code&gt;을 열면 React 앱이 실행된다.&lt;/p&gt;
&lt;p&gt;컴포넌트 파일은 &lt;code&gt;src/components/&lt;/code&gt; 폴더를 만들어 관리하는 것이 일반적이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;JSX 문법 이해하기&lt;/h2&gt;
&lt;p&gt;JSX는 &lt;strong&gt;JavaScript 안에 HTML을 작성할 수 있게 해주는 문법&lt;/strong&gt;이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;안녕하세요!&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;React 시작!&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;JSX 주의사항&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;반드시 하나의 부모 요소로 감싸야 한다 (&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; 또는 &lt;code&gt;&amp;lt;&amp;gt;...&amp;lt;/&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class&lt;/code&gt; 대신 &lt;code&gt;className&lt;/code&gt;을 사용한다&lt;/li&gt;
&lt;li&gt;모든 태그는 반드시 닫아야 한다 (&lt;code&gt;&amp;lt;img /&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;컴포넌트 이해하기&lt;/h2&gt;
&lt;p&gt;컴포넌트는 UI를 구성하는 독립적인 조각이다. &lt;code&gt;src/components/&lt;/code&gt; 폴더 안에 파일을 만들어 관리한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;컴포넌트 만들기&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Header.jsx
function Header() {
  return (
    &amp;lt;header&amp;gt;
      &amp;lt;h1&amp;gt;나의 쇼핑몰&amp;lt;/h1&amp;gt;
    &amp;lt;/header&amp;gt;
  );
}

export default Header;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;다른 파일에서 불러오기&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// App.jsx
import Header from &amp;#39;./components/Header&amp;#39;;

function App() {
  return &amp;lt;Header /&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Props — 컴포넌트에 데이터 전달하기&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Props를 사용하면 컴포넌트에 데이터를 넘겨줄 수 있다. 구조분해할당을 쓰면 코드가 더 간결해진다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Product.jsx
function Product({ name, price }) {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h3&amp;gt;{name}&amp;lt;/h3&amp;gt;
      &amp;lt;div&amp;gt;{price}&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default Product;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// 사용할 때
&amp;lt;Product name=&amp;quot;언젠가 입을 수 있는 스웨터&amp;quot; price=&amp;quot;13,000원&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;React에서 CSS 사용하기&lt;/h2&gt;
&lt;p&gt;React에서 스타일을 적용하는 방법은 크게 세 가지다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;① 일반 CSS 파일&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;가장 기본적인 방법. &lt;code&gt;.css&lt;/code&gt; 파일을 만들고 import해서 사용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Header.css */
.header {
  background: #1a1a2e;
  color: white;
  padding: 1rem;
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// Header.jsx
import &amp;#39;./Header.css&amp;#39;;

function Header() {
  return &amp;lt;header className=&amp;quot;header&amp;quot;&amp;gt;나의 쇼핑몰&amp;lt;/header&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;② CSS Module&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.module.css&lt;/code&gt; 파일을 사용하면 스타일이 컴포넌트 단위로 격리되어 클래스명 충돌을 방지할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Header.module.css */
.header {
  background: #1a1a2e;
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// Header.jsx
import styles from &amp;#39;./Header.module.css&amp;#39;;

function Header() {
  return &amp;lt;header className={styles.header}&amp;gt;나의 쇼핑몰&amp;lt;/header&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;③ Inline Style&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JSX에 직접 스타일 객체를 작성하는 방법. 간단한 스타일에 적합하다&lt;/p&gt;
&lt;h1&gt;React 입문: 개념부터 프로젝트 구조까지 한 번에 정리&lt;/h1&gt;
&lt;h2&gt;1. React란 무엇인가?&lt;/h2&gt;
&lt;p&gt;React는 Facebook(현 Meta)이 만든 &lt;strong&gt;UI(User Interface) 라이브러리&lt;/strong&gt;다. 웹 페이지의 화면을 구성하는 컴포넌트들을 효율적으로 렌더링하기 위해 만들어졌다.&lt;/p&gt;
&lt;p&gt;React의 핵심 개념은 &lt;strong&gt;Virtual DOM&lt;/strong&gt;이다. 브라우저의 실제 DOM을 직접 조작하면 성능이 느려지는데, React는 메모리 상에 가상의 DOM을 두고 변경된 부분만 비교해서 실제 DOM에 최소한으로 반영한다. 그 결과 화면이 빠르고 부드럽게 업데이트된다.&lt;/p&gt;
&lt;p&gt;또 한 가지 중요한 특징은 &lt;strong&gt;선언형(Declarative) 방식&lt;/strong&gt;이다. “이 버튼을 클릭하면 이 요소를 찾아 텍스트를 바꿔라”처럼 어떻게 할지를 일일이 명령하는 게 아니라, “이 상태일 때 화면은 이렇게 생겼다”고 &lt;strong&gt;결과물을 선언&lt;/strong&gt;한다. 상태가 바뀌면 React가 알아서 화면을 다시 그려준다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. Node.js가 필요한 이유&lt;/h2&gt;
&lt;p&gt;React 자체는 브라우저에서 동작하는 JavaScript 라이브러리다. 그런데 왜 &lt;strong&gt;Node.js&lt;/strong&gt;가 필요할까?&lt;/p&gt;
&lt;p&gt;React 프로젝트를 개발하다 보면 다음과 같은 작업들이 필요하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JSX 코드를 브라우저가 이해할 수 있는 JavaScript로 &lt;strong&gt;변환(트랜스파일)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;여러 파일을 하나로 &lt;strong&gt;번들링(bundling)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;로컬에서 &lt;strong&gt;개발 서버&lt;/strong&gt; 실행&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm install&lt;/code&gt;로 &lt;strong&gt;외부 패키지 설치&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 모든 작업들이 Node.js 환경에서 실행된다. Node.js 자체가 웹 서버를 만들기 위한 것이라기보다는, React 개발을 위한 &lt;strong&gt;도구들의 실행 환경&lt;/strong&gt; 역할을 한다고 보면 된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. Vite를 사용하는 이유&lt;/h2&gt;
&lt;p&gt;예전에는 React 프로젝트를 시작할 때 &lt;strong&gt;Create React App(CRA)&lt;/strong&gt; 을 주로 사용했다. 그런데 요즘은 &lt;strong&gt;Vite&lt;/strong&gt;가 사실상 표준이 됐다. 이유는 단순하다: &lt;strong&gt;빠르기 때문이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CRA는 프로젝트를 시작할 때 모든 파일을 한꺼번에 번들링한다. 프로젝트가 커질수록 개발 서버 시작 시간이 몇 십 초씩 걸리기도 한다.&lt;/p&gt;
&lt;p&gt;Vite는 다르다. 개발할 때는 파일을 번들링하지 않고, 브라우저가 요청하는 파일만 그때그때 변환해서 내려준다. 덕분에 서버 시작이 거의 즉각적이고, 코드를 수정했을 때 화면에 반영되는 &lt;strong&gt;HMR(Hot Module Replacement)&lt;/strong&gt; 속도도 월등히 빠르다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;CRA&lt;/th&gt;
&lt;th&gt;Vite&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;개발 서버 시작&lt;/td&gt;
&lt;td&gt;느림 (전체 번들링)&lt;/td&gt;
&lt;td&gt;빠름 (필요한 파일만)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMR 속도&lt;/td&gt;
&lt;td&gt;느림&lt;/td&gt;
&lt;td&gt;빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;번들러&lt;/td&gt;
&lt;td&gt;Webpack&lt;/td&gt;
&lt;td&gt;Rollup (esbuild)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;4. Vite + React 프로젝트 구조 파악&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;npm create vite@latest&lt;/code&gt;로 프로젝트를 만들면 아래와 같은 구조가 생긴다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my-app/
├── public/
├── src/
├── node_modules/
├── .gitignore
├── package.json
├── package-lock.json
├── README.md
└── vite.config.js&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;4-1. &lt;code&gt;public/&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;정적 파일을 보관하는 폴더다. 이 안에 있는 파일들은 빌드 과정에서 가공 없이 그대로 복사된다. 파비콘(&lt;code&gt;favicon.ico&lt;/code&gt;)이나 Open Graph 이미지처럼 코드에서 import하지 않고 URL로 직접 참조하는 파일들을 여기에 넣는다.&lt;/p&gt;
&lt;h3&gt;4-2. &lt;code&gt;node_modules/&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt;로 설치한 외부 패키지들이 실제로 저장되는 폴더다. 용량이 매우 크기 때문에 Git에는 올리지 않는다. &lt;code&gt;package.json&lt;/code&gt;만 있으면 언제든 &lt;code&gt;npm install&lt;/code&gt;로 다시 생성할 수 있다.&lt;/p&gt;
&lt;h3&gt;4-3. &lt;code&gt;.gitignore&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Git이 추적하지 않을 파일 목록을 정의한다. &lt;code&gt;node_modules/&lt;/code&gt;, 빌드 결과물인 &lt;code&gt;dist/&lt;/code&gt;, 환경변수 파일인 &lt;code&gt;.env&lt;/code&gt; 등을 여기에 등록해서 실수로 커밋되는 걸 막는다.&lt;/p&gt;
&lt;h3&gt;4-4. &lt;code&gt;package-lock.json&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;프로젝트에 설치된 패키지들의 정확한 버전을 기록한 파일이다. 팀원들이 &lt;code&gt;npm install&lt;/code&gt;을 실행했을 때 모두 같은 버전의 패키지를 설치하도록 보장해준다. 직접 편집하지 않는다.&lt;/p&gt;
&lt;h3&gt;4-5. &lt;code&gt;package.json&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;프로젝트의 메타 정보와 의존성 목록을 담은 파일이다. 프로젝트 이름, 버전, 사용 중인 패키지 목록, 그리고 &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;preview&lt;/code&gt; 같은 스크립트 명령어들이 여기에 정의된다.&lt;/p&gt;
&lt;h3&gt;4-6. &lt;code&gt;README.md&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;프로젝트 설명 문서다. GitHub에 올리면 저장소 메인 페이지에 렌더링되어 보여진다. 프로젝트 소개, 실행 방법, 사용 기술 스택 등을 적는다.&lt;/p&gt;
&lt;h3&gt;4-7. &lt;code&gt;vite.config.js&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Vite의 설정 파일이다. 개발 서버 포트 변경, 빌드 경로 설정, 플러그인 추가(예: &lt;code&gt;@vitejs/plugin-react&lt;/code&gt;) 등을 여기서 관리한다.&lt;/p&gt;
&lt;h3&gt;4-8. &lt;code&gt;src/&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;실제로 코드를 작성하는 폴더다. 컴포넌트 파일(&lt;code&gt;.jsx&lt;/code&gt;), 스타일, 이미지, 유틸 함수 등 개발자가 만드는 모든 소스 파일이 여기에 들어간다. 이 안의 파일들은 Vite가 번들링 과정에서 처리한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. JSX란 무엇인가?&lt;/h2&gt;
&lt;p&gt;React 코드를 처음 보면 JavaScript 안에 HTML이 섞여 있는 것처럼 보인다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function Greeting() {
  const name = &amp;quot;준태&amp;quot;;
  return (
    &amp;lt;div className=&amp;quot;greeting&amp;quot;&amp;gt;
      &amp;lt;h1&amp;gt;안녕하세요, {name}!&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;React 공부 중입니다.&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이게 바로 &lt;strong&gt;JSX(JavaScript XML)&lt;/strong&gt; 다. JavaScript 코드 안에서 HTML과 유사한 문법으로 UI 구조를 표현할 수 있게 해주는 &lt;strong&gt;문법적 확장(syntactic sugar)&lt;/strong&gt; 이다.&lt;/p&gt;
&lt;p&gt;브라우저는 JSX를 직접 이해하지 못한다. Vite와 Babel이 JSX를 아래처럼 순수한 JavaScript 함수 호출로 변환해준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;React.createElement(&amp;quot;div&amp;quot;, { className: &amp;quot;greeting&amp;quot; },
  React.createElement(&amp;quot;h1&amp;quot;, null, &amp;quot;안녕하세요, &amp;quot;, name, &amp;quot;!&amp;quot;),
  React.createElement(&amp;quot;p&amp;quot;, null, &amp;quot;React 공부 중입니다.&amp;quot;)
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JSX를 사용할 때 주의할 점이 몇 가지 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTML의 &lt;code&gt;class&lt;/code&gt; 대신 &lt;code&gt;className&lt;/code&gt;을 사용한다 (&lt;code&gt;class&lt;/code&gt;는 JavaScript 예약어)&lt;/li&gt;
&lt;li&gt;모든 태그는 반드시 닫혀야 한다 (&lt;code&gt;&amp;lt;img /&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;br /&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;반환값은 반드시 &lt;strong&gt;하나의 루트 요소&lt;/strong&gt;로 감싸야 한다&lt;/li&gt;
&lt;li&gt;JavaScript 표현식은 &lt;code&gt;{}&lt;/code&gt;로 감싸서 삽입한다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;6. React Component란?&lt;/h2&gt;
&lt;p&gt;React의 모든 UI는 &lt;strong&gt;컴포넌트(Component)&lt;/strong&gt; 로 이루어져 있다. 컴포넌트는 UI를 독립적이고 재사용 가능한 단위로 쪼갠 것이다.&lt;/p&gt;
&lt;p&gt;레고 블록을 생각하면 쉽다. 헤더, 버튼, 카드, 모달 같은 UI 요소들을 각각 독립된 블록으로 만들고, 이걸 조합해서 전체 페이지를 완성한다.&lt;/p&gt;
&lt;p&gt;현대 React에서는 &lt;strong&gt;함수형 컴포넌트&lt;/strong&gt;를 사용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// Button 컴포넌트
function Button({ label, onClick }) {
  return (
    &amp;lt;button onClick={onClick}&amp;gt;
      {label}
    &amp;lt;/button&amp;gt;
  );
}

// App 컴포넌트에서 Button을 재사용
function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Button label=&amp;quot;확인&amp;quot; onClick={() =&amp;gt; alert(&amp;quot;확인!&amp;quot;)} /&amp;gt;
      &amp;lt;Button label=&amp;quot;취소&amp;quot; onClick={() =&amp;gt; alert(&amp;quot;취소!&amp;quot;)} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;컴포넌트는 다음 규칙을 따른다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이름은 반드시 &lt;strong&gt;대문자&lt;/strong&gt;로 시작한다 (소문자면 HTML 태그로 인식)&lt;/li&gt;
&lt;li&gt;JSX를 반환하는 JavaScript 함수다&lt;/li&gt;
&lt;li&gt;자신만의 상태(state)와 로직을 가질 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;7. Props란?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Props(Properties)&lt;/strong&gt; 는 부모 컴포넌트가 자식 컴포넌트에게 데이터를 전달하는 방법이다. HTML에서 &lt;code&gt;&amp;lt;img src=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt; 처럼 속성을 넘기는 것과 비슷하다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// 부모 컴포넌트
function App() {
  return (
    &amp;lt;UserCard
      name=&amp;quot;김준태&amp;quot;
      role=&amp;quot;Frontend Developer&amp;quot;
      isActive={true}
    /&amp;gt;
  );
}

// 자식 컴포넌트 - props를 받아서 사용
function UserCard({ name, role, isActive }) {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;{name}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;{role}&amp;lt;/p&amp;gt;
      &amp;lt;span&amp;gt;{isActive ? &amp;quot;활동 중&amp;quot; : &amp;quot;비활동&amp;quot;}&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Props의 핵심 특성은 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;단방향 데이터 흐름&lt;/strong&gt;: 데이터는 항상 부모 → 자식 방향으로만 흐른다. 자식이 부모의 데이터를 직접 바꿀 수 없다. 자식에서 부모로 무언가 전달하고 싶다면, 부모가 함수를 props로 넘겨주고 자식이 그 함수를 호출하는 방식을 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;읽기 전용&lt;/strong&gt;: 자식 컴포넌트는 받은 props를 직접 수정할 수 없다. 데이터의 흐름을 예측 가능하게 유지하기 위한 규칙이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;어떤 값이든 전달 가능&lt;/strong&gt;: 문자열, 숫자, 배열, 객체, 함수, 심지어 다른 컴포넌트도 props로 전달할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;이 내용들이 React의 첫 번째 허들이라고 할 수 있다. 컴포넌트 → JSX → Props 이 세 가지 개념이 서로 맞물려서 React 앱 전체를 구성하는 기반이 된다. 다음 글에서는 상태 관리의 핵심인 &lt;code&gt;useState&lt;/code&gt;와 &lt;code&gt;useEffect&lt;/code&gt;를 다뤄볼 예정이다.&lt;/p&gt;</description>
      <category>Front_end</category>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/8</guid>
      <comments>https://jae-oan.tistory.com/8#entry8comment</comments>
      <pubDate>Wed, 25 Feb 2026 16:25:01 +0900</pubDate>
    </item>
    <item>
      <title>리엑트 server.js 예외처리방법</title>
      <link>https://jae-oan.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;예외 처리 방식&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;API_URL&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;`링크내용~~&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&lt;a href=&quot;https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=&quot;&gt;:generateContent?key=&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;try&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;fetch&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;API_URL&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;method&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'POST'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'Content-Type'&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'application/json'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; },&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;contents&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; }),&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 할 때 오류가 많이 발생하는데&amp;nbsp;&lt;br /&gt;보통 자바스크립트는 에러가 발생해도 어디서 구체적으로 발생한 것인지 알기 어려울 때가 많아서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 처리 구문을 사용하여 에러 발생 위치를 잡는다.&lt;/p&gt;
&lt;h1 id=&quot;trycatchfinally&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Try/Catch 사용 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try { 구문 } 안에&amp;nbsp; catch를 넣어서 try 블록에서 예외가 발생하며 잡아내는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;try&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;fetch&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;API_URL&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;method&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'POST'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'Content-Type'&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'application/json'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; },&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;contents&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; }),&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 구글 측 에러 처리&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;ok&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;) &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;errorData&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'  Google API 에러 발생:'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;errorData&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;)); &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;({ &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'Gemini API 호출 실패'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;details&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;errorData&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'알 수 없는 오류'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;aiResponseText&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;candidates&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;].&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;parts&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;].&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'✅ Gemini 응답 성공'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;aiResponseText&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; });&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;} &lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커톤 때 ai를 사용하였는데 오류가 생기면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠르게 서버 터미널에 나오는 json을 ai한테 보내고 오류를 잡았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;237&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vTJcr/dJMcaf6j3Px/NYz12dOpmQQrK0dtvkWp11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vTJcr/dJMcaf6j3Px/NYz12dOpmQQrK0dtvkWp11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vTJcr/dJMcaf6j3Px/NYz12dOpmQQrK0dtvkWp11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvTJcr%2FdJMcaf6j3Px%2FNYz12dOpmQQrK0dtvkWp11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;237&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;237&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc;&quot;&gt;&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 3. 데이터 가공 (Google 규격에 맞게 변환)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// system 역할은 지원하지 않는 경우가 많아 user로 치환&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;contents&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;history&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;msg&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; ({&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;role&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;msg&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;role&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'system'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'user'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;msg&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;role&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;parts&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; [{ &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;msg&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; }]&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; }));&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;history는 배열인데 지금까지 대화 목록이 담겨 있다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{ &lt;span&gt;&quot;role&quot;&lt;/span&gt;: &lt;span&gt;&quot;user&quot;&lt;/span&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&quot;content&quot;&lt;/span&gt;: &lt;span&gt;&quot;안녕?&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 json은 이렇게 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&quot;role&quot;:&amp;nbsp;&quot;user&quot;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&quot;parts&quot;: [&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;text&quot;: &quot;안녕?&quot;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;] &lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini 한테 내용을 보낼 때&amp;nbsp; parts 가 추가된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// Google Gemini API 호출&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;API_URL&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;`&lt;a href=&quot;https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=&quot;&gt;https://generativelanguage.goog~~~~&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;try&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;fetch&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;API_URL&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;method&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'POST'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'Content-Type'&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'application/json'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; },&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;contents&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; }), &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 구글 측 에러 처리&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;ok&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;errorData&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'  Google API 에러 발생:'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;errorData&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;)); &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;apiResponse&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;({ &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'Gemini API 호출 실패'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;details&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;errorData&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'알 수 없는 오류'&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/h3&gt;
&lt;h3 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;1. AI 채팅 API (POST /api/chat)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;용도:&lt;/b&gt; 프론트엔드 대신 Google Gemini에게 질문을 전달하고 답변을 받아옴 (API Key 보안).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;사용법 (Frontend):&lt;/b&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const response = await fetch('/api/chat', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ 
    history: [
      { role: 'user', content: '안녕' },
      { role: 'model', content: '반가워요' },
      { role: 'user', content: '햄버거 추천해줘' }
    ] 
  })
});
const data = await response.json();
console.log(data.response); // AI의 답변 출력
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;주의사항:&lt;/b&gt; history 배열 필수 (과거 대화 맥락 유지용).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;4&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;2. 로그 저장 API (POST /api/event-logs/events)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;용도:&lt;/b&gt; 사용자의 클릭, 터치 등 행동 로그를 &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;6,0,0&quot;&gt;외부 백엔드 서버&lt;/b&gt;로 전달하여 저장.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;사용법 (Frontend):&lt;/b&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;await fetch('/api/event-logs/events', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    eventType: 'click',
    x: 100,
    y: 200,
    timestamp: Date.now()
  })
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;7&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;3. 클릭 좌표 조회 API (GET /api/event-logs/click)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;용도:&lt;/b&gt; 특정 페이지(pageNum)의 클릭 좌표 데이터를 외부 서버에서 가져옴 (히트맵 그릴 때 사용).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;사용법 (Frontend):&lt;/b&gt;&lt;/li&gt;
&lt;li data-ved=&quot;0CAAQhtANahgKEwjGv-HU-NqRAxUAAAAAHQAAAAAQnAo&quot; data-hveid=&quot;0&quot;&gt;&amp;nbsp;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;// pageNum=1 인 페이지의 데이터 요청
const res = await fetch('/api/event-logs/click?pageNum=1');
const data = await res.json(); 
// data: [{x: 50, y: 50}, {x: 120, y: 30}, ...]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;JavaScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;10&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;4. 체류 시간 조회 API (GET /api/event-logs/times/average)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;용도:&lt;/b&gt; 사용자가 특정 페이지에 머문 평균 시간을 외부 서버에서 가져옴.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;사용법 (Frontend):&lt;/b&gt;
&lt;div&gt;&lt;span&gt;JavaScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;const res = await fetch('/api/event-logs/times/average?pageNum=1');
const data = await res.json();
console.log(data); // 예: { averageTime: 4.5 } (초 단위)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Front_end</category>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/7</guid>
      <comments>https://jae-oan.tistory.com/7#entry7comment</comments>
      <pubDate>Thu, 1 Jan 2026 18:06:02 +0900</pubDate>
    </item>
    <item>
      <title>React 개발자를 위한 가장 쉬운 Node.js 백엔드 연동 가이드</title>
      <link>https://jae-oan.tistory.com/6</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;298&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbzbwd/dJMcai2G0rr/Jfnof880kcHnGaFhPZigyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbzbwd/dJMcai2G0rr/Jfnof880kcHnGaFhPZigyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbzbwd/dJMcai2G0rr/Jfnof880kcHnGaFhPZigyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbzbwd%2FdJMcai2G0rr%2FJfnof880kcHnGaFhPZigyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;298&quot; height=&quot;169&quot; data-origin-width=&quot;298&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 백엔드 서버 구축하기 (Node.js &amp;amp; Express)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React로 만든 프론트엔드 애플리케이션은 결국 데이터를 주고받거나 완성된 HTML 파일을 사용자에게 제공할 '서버'가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;b&gt;Node.js&lt;/b&gt;와 &lt;b&gt;Express&lt;/b&gt;를 사용해 간단한 웹 서버를 만들어 보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1단계: 개발 환경 준비&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Node.js 설치:&lt;/b&gt; 구글에서 'Node.js'를 검색하여 공식 사이트에서 &lt;b&gt;LTS (Long Term Support)&lt;/b&gt; 버전을 다운로드하여 설치합니다. Node.js를 설치하면 패키지 관리 도구인 &lt;b&gt;npm&lt;/b&gt;도 함께 설치됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작업 폴더 생성:&lt;/b&gt; 프로젝트를 진행할 폴더를 하나 만듭니다. (예: &lt;code&gt;my-project&lt;/code&gt;) 폴더 이름은 가급적 영어로 설정하는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;에디터로 열기:&lt;/b&gt; VS Code와 같은 코드 에디터로 방금 만든 폴더를 엽니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2단계: 서버 파일 작성 및 설정&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;server.js&lt;/code&gt; 파일 생성:&lt;/b&gt; 폴더 내에 &lt;code&gt;server.js&lt;/code&gt;라는 이름의 파일을 만듭니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기본 서버 코드 작성:&lt;/b&gt; 아래 코드를 &lt;code&gt;server.js&lt;/code&gt; 파일에 입력합니다.
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const express = require('express');
const path = require('path');
const app = express();

app.listen(8080, function () {
  console.log('8080 포트에서 서버가 대기 중입니다...');
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;express&lt;/code&gt;는 Node.js에서 웹 서버를 쉽게 만들 수 있게 도와주는 &lt;b&gt;프레임워크&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app.listen(8080, ...)&lt;/code&gt;은 8080번 포트에서 사용자의 요청을 기다리도록 서버를 실행하는 명령어입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;package.json&lt;/code&gt; 생성:&lt;/b&gt; 터미널을 열고(VS Code&amp;nbsp; 터미널 사용 가능) 다음 명령어를 입력합니다.
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 명령어는 현재 폴더를 Node.js 프로젝트로 초기화하고, 프로젝트의 정보와 의존성을 관리하는 &lt;code&gt;package.json&lt;/code&gt; 파일을 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Express 설치:&lt;/b&gt; 서버 코드에서 &lt;code&gt;require('express')&lt;/code&gt;를 사용했으므로, 실제로 Express 라이브러리를 설치해야 합니다.
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install express
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3단계: 서버 실행하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 서버를 실행할 준비가 되었습니다. 터미널에 다음 명령어를 입력하세요.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;8080 포트에서 서버가 대기 중입니다...&quot;라는 메시지가 뜨면 성공입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;nodemon&lt;/code&gt; 사용하기&lt;/b&gt;&lt;br /&gt;&lt;code&gt;node server.js&lt;/code&gt;로 서버를 실행하면, 코드를 수정할 때마다 서버를 껐다가 다시 켜야 하는 번거로움이 있습니다. &lt;code&gt;nodemon&lt;/code&gt;을 사용하면 코드가 변경될 때마다 서버를 자동으로 재시작해 줍니다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;# nodemon 설치 (컴퓨터 전역에 설치)
npm install -g nodemon

# nodemon으로 서버 실행
nodemon server.js
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2부: 프론트엔드 구축하기 (React)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 사용자에게 보여질 화면을 만들 React 프로젝트를 생성해 보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1단계: React 프로젝트 생성 (Create React App)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 서버 폴더와 나란히 'client' 또는 'frontend' 같은 이름의 하위 폴더를 만들어 그 안에 React 프로젝트를 구성합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Node.js 설치:&lt;/b&gt; 1부에서 이미 설치했다면 통과합니다. 최신 버전인지 확인해 주세요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;React 앱 생성:&lt;/b&gt; 터미널에서 다음을 입력합니다.
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;npx create-react-app my-app
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;my-app&lt;/code&gt;은 원하는 프로젝트 이름으로 변경할 수 있습니다. (띄어쓰기 금지)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npx&lt;/code&gt;는 라이브러리를 설치하지 않고 일회성으로 실행하는 명령어입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(참고) &lt;code&gt;Vite&lt;/code&gt; 사용하기&lt;/b&gt;&lt;br /&gt;최근에는 &lt;code&gt;create-react-app&lt;/code&gt;(CRA)보다 빌드 속도가 훨씬 빠른 &lt;code&gt;Vite&lt;/code&gt;를 사용하는 추세입니다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# Vite로 React 프로젝트 생성
npm create vite@latest
# (이후 React, Javascript 등 옵션 선택)

# 생성된 폴더로 이동
cd [프로젝트명]
# 의존성 설치
npm install
# 개발 서버 실행
npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2단계: 개발 및 빌드&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;프로젝트 폴더로 이동 및 실행:&lt;/b&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;cd my-app
npm start
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;npm start&lt;/code&gt; (CRA) 또는 &lt;code&gt;npm run dev&lt;/code&gt; (Vite) 명령어를 입력하면 개발 서버가 실행되고, 브라우저에서 실시간으로 변경 사항을 확인할 수 있는 페이지가 열립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발:&lt;/b&gt; &lt;code&gt;src&lt;/code&gt; 폴더 내의 파일들을 수정하며 원하는 UI와 기능을 개발합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빌드 (Build):&lt;/b&gt;&lt;br /&gt;개발이 완료되면, 이 React 프로젝트를 실제 서버에 배포할 수 있는 정적 파일(HTML, CSS, JS)로 변환해야 합니다.
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 명령어를 실행하면 &lt;code&gt;build&lt;/code&gt;라는 폴더가 생성되고, 그 안에 압축되고 최적화된 파일들이 담깁니다.&lt;/li&gt;
&lt;li&gt;이제 1부에서 만든 &lt;b&gt;Node.js 서버가&lt;/b&gt; 이 &lt;code&gt;build&lt;/code&gt; 폴더 안의 &lt;code&gt;index.html&lt;/code&gt; 파일을 사용자에게 보내주도록 코드를 추가하면, 우리가 만든 React 앱을 서비스할 수 있게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3부: React 핵심 기능 익히기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 프로젝트를 만들었다면, 이제 자주 사용하는 핵심 기능들을 알아봅시다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 스타일링: &lt;code&gt;styled-components&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS를 JS 파일 내에서 컴포넌트 스타일로 직접 작성하게 해주는 라이브러리입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;설치:&lt;/b&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm i styled-components
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용법:&lt;/b&gt;&lt;br /&gt;&lt;code&gt;import styled from &quot;styled-components&quot;;&lt;/code&gt;를 파일 상단에 추가하고, 스타일을 적용할 HTML 태그를 지정하여 컴포넌트를 만듭니다.
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import styled from &quot;styled-components&quot;;

// 'button' 태그에 스타일을 적용한 'StyledButton' 컴포넌트 생성
const StyledButton = styled.button`
  background-color: blue;
  color: white;
  font-size: 16px;
  padding: 10px;
`;

function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;StyledButton&amp;gt;클릭하세요&amp;lt;/StyledButton&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 페이지 나누기: &lt;code&gt;react-router-dom&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React는 기본적으로 '싱글 페이지 애플리케이션'(SPA)이므로, 여러 페이지를 만들려면 라우팅 라이브러리가 필요합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;설치:&lt;/b&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install react-router-dom
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기본 설정 (&lt;code&gt;index.js&lt;/code&gt;):&lt;/b&gt;&lt;br /&gt;&lt;code&gt;src/index.js&lt;/code&gt; 파일에서 &lt;code&gt;&amp;lt;App /&amp;gt;&lt;/code&gt; 컴포넌트를 &lt;code&gt;&amp;lt;BrowserRouter&amp;gt;&lt;/code&gt;로 감싸줍니다.
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;BrowserRouter&amp;gt;
      &amp;lt;App /&amp;gt;
    &amp;lt;/BrowserRouter&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라우트(페이지) 정의 (&lt;code&gt;App.js&lt;/code&gt;):&lt;/b&gt;&lt;br /&gt;&lt;code&gt;App.js&lt;/code&gt;에서 &lt;code&gt;&amp;lt;Routes&amp;gt;&lt;/code&gt;와 &lt;code&gt;&amp;lt;Route&amp;gt;&lt;/code&gt; 컴포넌트를 사용해 URL 경로별로 보여줄 컴포넌트를 지정합니다.
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;// App.js
import { Routes, Route } from 'react-router-dom';
import DetailPage from './Detail'; // 상세 페이지 컴포넌트
import AboutPage from './About'; // 어바웃 페이지 컴포넌트

function App() {
  return (
    &amp;lt;Routes&amp;gt;
      {/* path=&quot;/&quot; 는 메인 페이지입니다. */}
      &amp;lt;Route path=&quot;/&quot; element={ &amp;lt;div&amp;gt;메인페이지임&amp;lt;/div&amp;gt; } /&amp;gt;
      &amp;lt;Route path=&quot;/detail&quot; element={ &amp;lt;DetailPage /&amp;gt; } /&amp;gt;
      &amp;lt;Route path=&quot;/about&quot; element={ &amp;lt;AboutPage /&amp;gt; } /&amp;gt;
    &amp;lt;/Routes&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동적 라우트 (&lt;code&gt;useParams&lt;/code&gt;):&lt;/b&gt;&lt;br /&gt;&lt;code&gt;/detail/1&lt;/code&gt;, &lt;code&gt;/detail/2&lt;/code&gt;처럼 URL 파라미터를 받는 페이지를 만들 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;라우트 설정 (&lt;code&gt;App.js&lt;/code&gt;):&lt;/b&gt;&lt;br /&gt;경로 뒤에 &lt;code&gt;/:id&lt;/code&gt; 와 같이 변수명을 지정합니다.
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;&amp;lt;Route path=&quot;/detail/:id&quot; element={&amp;lt;Detail shoes={shoes}/&amp;gt;} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파라미터 사용 (&lt;code&gt;Detail.js&lt;/code&gt;):&lt;/b&gt;&lt;br /&gt;&lt;code&gt;useParams&lt;/code&gt; 훅(Hook)을 사용하여 URL의 &lt;code&gt;:id&lt;/code&gt; 값을 가져올 수 있습니다.
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// Detail.js
import { useParams } from 'react-router-dom';

function Detail(props) {
  let { id } = useParams(); // URL의 /:id 값을 가져옴
  // id 변수에는 &quot;1&quot;, &quot;2&quot; 같은 값이 들어옵니다.

  // props.shoes 배열에서 id에 맞는 상품을 찾아 보여줌
  // (id는 문자열이므로 숫자로 변환 필요)
  let product = props.shoes.find(item =&amp;gt; item.id == id);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h4&amp;gt;{product.title}&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;{product.content}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 컴포넌트 생명주기: &lt;code&gt;useEffect&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useEffect&lt;/code&gt;는 React 컴포넌트가 화면에 나타나거나(mount), 사라지거나(unmount), 업데이트(update)될 때 특정 코드를 실행할 수 있게 해주는 훅입니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useEffect, useState } from 'react';

function MyComponent() {
  useEffect(() =&amp;gt; {
    // 1. 재렌더링마다 실행됨 (Mount + Update)
    console.log('컴포넌트가 렌더링되었습니다.');
  });

  useEffect(() =&amp;gt; {
    // 2. Mount시 1회만 실행됨
    // (예: 처음 로드될 때 서버에서 데이터 가져오기)
    console.log('컴포넌트가 처음 마운트되었습니다.');
  }, []); // &amp;lt;-- 두 번째 인자로 빈 배열(Dependency Array) 전달

  useEffect(() =&amp;gt; {
    // 3. Unmount시 1회 실행됨 (Cleanup 함수)
    // (예: 컴포넌트가 사라질 때 타이머 해제 등)
    return () =&amp;gt; {
      console.log('컴포넌트가 언마운트됩니다.');
    }
  }, []);

  return &amp;lt;div&amp;gt;내 컴포넌트&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 서버와 통신 (AJAX): &lt;code&gt;axios&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 앱에서 서버와 데이터를 주고받을 때(AJAX) &lt;code&gt;fetch&lt;/code&gt;를 사용하거나, 더 편리한 &lt;code&gt;axios&lt;/code&gt; 라이브러리를 주로 사용합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;설치:&lt;/b&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install axios
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GET 요청 (데이터 가져오기):&lt;/b&gt;&lt;br /&gt;서버에 데이터를 요청할 때 사용합니다. &lt;code&gt;useEffect&lt;/code&gt;와 함께 사용하여 컴포넌트가 마운트될 때 데이터를 가져오는 경우가 많습니다.
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;import axios from 'axios';

// ...

// 버튼 클릭 시 데이터 가져오기
&amp;lt;button onClick={() =&amp;gt; {
  axios.get('https://codingapple1.github.io/shop/data2.json')
    .then((result) =&amp;gt; {
      // 요청 성공 시
      console.log(result.data); // 서버가 보낸 데이터
    })
    .catch((error) =&amp;gt; {
      // 요청 실패 시
      console.error('데이터를 가져오는 데 실패했습니다:', error);
    });
}}&amp;gt;데이터 요청&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;POST 요청 (데이터 보내기):&lt;/b&gt;&lt;br /&gt;서버에 데이터를 전송할 때(예: 로그인, 회원가입) 사용합니다.
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;axios.post('https://서버URL/login', {
  username: 'kim',
  password: '123'
})
.then((result) =&amp;gt; {
  console.log('전송 성공:', result.data);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동시 요청:&lt;/b&gt;&lt;br /&gt;여러 개의 요청을 동시에 보내고, 모든 요청이 완료되었을 때 처리할 수 있습니다.
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;Promise.all([
  axios.get('https://서버URL/data1'),
  axios.get('https://서버URL/data2')
])
.then(([result1, result2]) =&amp;gt; {
  // 두 요청이 모두 성공했을 때 실행
  console.log('데이터 1:', result1.data);
  console.log('데이터 2:', result2.data);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4부: 고급 상태 관리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useState&lt;/code&gt;로 상태를 관리하는 것이 기본이지만, 앱의 규모가 커지면 여러 컴포넌트가 동일한 상태를 공유해야 하는 복잡한 상황이 발생합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 전역 상태 관리: &lt;code&gt;Redux Toolkit&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Redux&lt;/code&gt;는 모든 컴포넌트가 공유하는 &lt;b&gt;전역 상태 저장소(Store)&lt;/b&gt;를 만들어 상태 관리를 용이하게 합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;설치:&lt;/b&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm install @reduxjs/toolkit react-redux
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;store.js&lt;/code&gt; 셋팅:&lt;/b&gt;&lt;br /&gt;&lt;code&gt;src&lt;/code&gt; 폴더에 &lt;code&gt;store.js&lt;/code&gt; 파일을 만들고 아래 코드를 작성합니다. 이 파일이 상태(state)들을 보관하는 저장소입니다.
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;// src/store.js
import { configureStore } from '@reduxjs/toolkit';

export default configureStore({
  reducer: {
    // 여기에 state들을 등록합니다. (예: user: userSlice.reducer)
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;index.js&lt;/code&gt; 셋팅:&lt;/b&gt;&lt;br /&gt;&lt;code&gt;index.js&lt;/code&gt;에서 앱 전체를 &lt;code&gt;&amp;lt;Provider&amp;gt;&lt;/code&gt;로 감싸고, 위에서 만든 &lt;code&gt;store&lt;/code&gt;를 주입합니다.
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// index.js
import { Provider } from 'react-redux';
import store from './store.js'; // 방금 만든 store.js

root.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;Provider store={store}&amp;gt;
      &amp;lt;BrowserRouter&amp;gt;
        &amp;lt;App /&amp;gt;
      &amp;lt;/BrowserRouter&amp;gt;
    &amp;lt;/Provider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
이제 앱 내의 모든 컴포넌트가 Redux store에 접근할 수 있게 되었습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 서버 상태 관리: &lt;code&gt;React Query&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;React Query&lt;/code&gt;는 &lt;code&gt;axios&lt;/code&gt; 같은 AJAX 요청을 더욱 쉽고 강력하게 관리해 주는 라이브러리입니다. 특히 서버에서 받아온 데이터(서버 상태) 관리에 특화되어 있으며, 캐싱, 실시간 업데이트 등을 자동으로 처리해 줍니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;설치:&lt;/b&gt; (v3 기준)
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm install react-query@3 --legacy-peer-deps
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기본 사용법 (&lt;code&gt;useQuery&lt;/code&gt;):&lt;/b&gt;&lt;br /&gt;&lt;code&gt;useQuery&lt;/code&gt;는 데이터를 가져오는(GET) hook입니다.
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useQuery } from 'react-query';
import axios from 'axios';

function UserProfile() {
  // useQuery('작명', '데이터 가져오는 함수')
  let result = useQuery('userData', () =&amp;gt;
    axios.get('https://codingapple1.github.io/userdata.json')
      .then((a) =&amp;gt; { return a.data })
  );

  // result 객체에는 다양한 상태가 포함됩니다.
  if (result.isLoading) return &amp;lt;div&amp;gt;로딩 중...&amp;lt;/div&amp;gt;;
  if (result.error) return &amp;lt;div&amp;gt;에러 발생!&amp;lt;/div&amp;gt;;

  // 데이터 로딩 성공 시
  return &amp;lt;div&amp;gt;사용자 이름: {result.data.name}&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;code&gt;useQuery&lt;/code&gt;는 'userData'라는 이름으로 데이터를 캐싱하며, 다른 컴포넌트에서 동일한 이름으로 &lt;code&gt;useQuery&lt;/code&gt;를 호출하면 서버에 재요청하지 않고 캐시된 데이터를 즉시 반환하여 성능을 향상시킵니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front_end</category>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/6</guid>
      <comments>https://jae-oan.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 3 Nov 2025 18:50:08 +0900</pubDate>
    </item>
    <item>
      <title>C++ 인라인 함수,템플릿</title>
      <link>https://jae-oan.tistory.com/5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. 인라인 함수&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;함수 호출 시 발생하는 오버헤드를 줄이기 위해서 함수를 호출하는 대신 함수가 호출되는 곳마다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;함수의 코드를 복사하여 넣어주는 특별한 함수&lt;/p&gt;
&lt;pre id=&quot;code_1758874105424&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

// 홀수인지 판별하는 함수
int odd(int x) {
    return (x % 2);
}

int main() {
    int sum = 0;

    // 1에서 10000까지의 홀수의 합 계산
    for (int i = 1; i &amp;lt;= 10000; i++) {
        if (odd(i)) {
            sum += i;
        }
    }

    cout &amp;lt;&amp;lt; &quot;1부터 10000까지 홀수의 합 = &quot; &amp;lt;&amp;lt; sum &amp;lt;&amp;lt; endl;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수 호출의 숨겨진 비용, 오버헤드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for 루프 등에서 odd()와 같이 아주 작은 함수를 수만 번 호출하면, 실제 함수 내용이 실행되는 시간보다 함수를 호출하고 돌아오는 과정에 드는 부가적인 시간이 더 커지는 비효율이 발생합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제점: 배보다 배꼽이 더 크다!&lt;/b&gt;   작은 크기의 함수를 반복 호출하면, 함수 실행 시간에 비해 호출을 위해 소요되는 &lt;b&gt;오버헤드&lt;/b&gt;가 상대적으로 매우 커집니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✨ 해답: 인라인 함수 (Inline Function)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인라인(inline) 함수&lt;/b&gt;는 이러한 오버헤드를 줄이기 위한 해결책입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 함수처럼 별도의 공간으로 이동하여 실행되는 것이 아니라, 컴파일 시점에 함수를 호출하는 곳에 함수 코드가 그대로 복사되어 삽입됩니다. 이 덕분에 함수 호출에 따른 시간 소모가 완전히 사라집니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;동작 방식&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반 함수&lt;/b&gt;: 함수 호출 &amp;rarr; 함수 코드로 점프 &amp;rarr; 실행 &amp;rarr; 원래 위치로 복귀&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인라인 함수&lt;/b&gt;: 컴파일러가 함수 호출 부분을 함수 코드로 대체 &amp;rarr; 함수 호출 과정 없이 바로 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;매크로(#define)와 유사점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 컴파일 전에 코드를 치환하는 **매크로(macro)**와 매우 유사합니다. 하지만 인라인 함수는 일반 함수처럼 타입 검사 등이 가능하여 매크로보다 훨씬 안전하고 권장되는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시))&lt;/p&gt;
&lt;pre id=&quot;code_1758874796641&quot; class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
inline int odd(int x) {
 return (x%2);
}
int main() {
 int sum = 0;
 for(int i=1; i&amp;lt;=10000; i++) {
 if(odd(i)) sum += i;
 }
 cout &amp;lt;&amp;lt; sum;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 사용하면 if(odd(i)) sum += i; 여기에 odd(i) 대신 i%2가 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디폴트 인자&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인자에 값이 넘어오지 않는 경우, 디폴트 값을 받도록 선언된 인자 : &amp;lsquo;인자 = 디폴트값&amp;rsquo; 형태로 선언&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758888816321&quot; class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;void default_sample(char c, int i, double d = 0.5 ); // 함수의 선언부
void main() {
 default_sample (&amp;lsquo;X', 10);
default_sample (&amp;lsquo;Y', 30, 2.0);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드처럼 double d=0.5 디폴트 인자를 함수의 선언부에 지정하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758889058342&quot; class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;void foo(char c = 'A', int i = 10, double d = 0.5);
foo('A', 20); // 세 번째 인자만 생략함
foo('B'); // 두 번째, 세 번째 인자만 생략함
foo(); // 인자 모두를 생략함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수로 쓸 때 가장 오른쪽 인자부터 생략해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1758889647311&quot; class=&quot;arduino&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
enum INT_TYPE {DECIMAL, OCTAL, HEXADECIMAL};
void PrintArray(const int arr[], int size = 5, INT_TYPE type = DECIMAL);
int main() {
 int arr1[] = {10, 20, 30, 40, 50};
 int arr2[10] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
 PrintArray(arr1);
 PrintArray(arr1, 5, HEXADECIMAL);
 PrintArray(arr2);
 PrintArray(arr2, 10);
 return 0;
}
void PrintArray(const int arr[], int size, INT_TYPE type) {
 cout.setf(ios::showbase);
 for(int i = 0 ; i &amp;lt; size ; i++) {
 switch( type ) {
 case DECIMAL: cout &amp;lt;&amp;lt; dec; break;
 case OCTAL: cout &amp;lt;&amp;lt; oct; break;
 case HEXADECIMAL: cout &amp;lt;&amp;lt; hex; break;
 }
 cout.width(5);
 cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &quot; &quot;;
 }
 cout &amp;lt;&amp;lt; endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예시 코드 해석&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 정수 배열을 &lt;b&gt;10진수(DECIMAL)&lt;/b&gt;, &lt;b&gt;8진수(OCTAL)&lt;/b&gt;, 또는 **16진수(HEXADECIMAL)**로 유연하게 출력하는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PrintArray 함수를 정의하고 사용하는 예제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**디폴트 매개변수(default parameter)**를 활용하여 함수 호출을 간결하게 만드는 것이 핵심입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;## 코드 주요 구성 요소&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. enum INT_TYPE&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enum은 특정 이름에 정수 값을 매겨주는 역할을 합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;C++&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;enum INT_TYPE {DECIMAL, OCTAL, HEXADECIMAL};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DECIMAL, OCTAL, HEXADECIMAL이라는 이름을 각각 정수 0, 1, 2에 대응시킵니다.&lt;/li&gt;
&lt;li&gt;숫자 0, 1, 2를 직접 쓰는 것보다 코드의 &lt;b&gt;가독성&lt;/b&gt;이 훨씬 좋아집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. PrintArray 함수&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열을 받아 화면에 출력하는 함수입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;C++&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;void PrintArray(const int arr[], int size = 5, INT_TYPE type = DECIMAL);
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;const int arr[]: 수정할 수 없는 정수 배열을 받습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;int size = 5&lt;/b&gt;: 출력할 원소의 개수를 정합니다. 만약 함수 호출 시 이 값을 생략하면 &lt;b&gt;기본값으로 5&lt;/b&gt;가 사용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;INT_TYPE type = DECIMAL&lt;/b&gt;: 출력할 진법을 정합니다. 생략하면 **기본값으로 DECIMAL(10진수)**이 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. main 함수&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램의 시작점으로, 두 개의 배열을 선언하고 PrintArray 함수를 네 가지 방식으로 호출합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;PrintArray(arr1);&lt;/b&gt;: size와 type을 생략했습니다. arr1의 원소 5개를 기본값인 10진수로 출력합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PrintArray(arr1, 5, HEXADECIMAL);&lt;/b&gt;: 모든 인자를 직접 지정했습니다. arr1의 원소 5개를 16진수로 출력합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PrintArray(arr2);&lt;/b&gt;: size와 type을 생략했습니다. arr2는 원소가 10개지만, size의 기본값인 &lt;b&gt;5&lt;/b&gt;가 적용되어 앞의 5개만 10진수로 출력합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PrintArray(arr2, 10);&lt;/b&gt;: type을 생략했습니다. arr2의 원소 10개를 기본값인 10진수로 출력합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;PrintArray 함수 상세 분석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 내부에서는 cout의 출력 서식을 변경하여 진법을 조절합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;cout.setf(ios::showbase);&lt;/b&gt;: 진법을 시각적으로 보여주는 접두사를 출력하게 합니다. (8진수는 0, 16진수는 0x)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;switch (type)&lt;/b&gt;: type 값에 따라 출력 서식을 변경합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cout &amp;lt;&amp;lt; dec;: 10진수 모드로 변경&lt;/li&gt;
&lt;li&gt;cout &amp;lt;&amp;lt; oct;: 8진수 모드로 변경&lt;/li&gt;
&lt;li&gt;cout &amp;lt;&amp;lt; hex;: 16진수 모드로 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cout.width(5);&lt;/b&gt;: 각 숫자가 출력될 때 차지할 최소 너비를 5칸으로 설정하여 정렬 효과를 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 결과&amp;nbsp;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;   10    20    30    40    50 
  0xa   0x14   0x1e   0x28   0x32 
   10    20    30    40    50 
   10    20    30    40    50    60    70    80    90   100 &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 중복의 약점- 중복 함수의 코드 중복&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758889963048&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
void myswap(int&amp;amp; a, int&amp;amp; b) {
int tmp;
tmp = a;
a = b;
b = tmp;
}
void myswap(double &amp;amp; a, double &amp;amp; b) {
double tmp;
tmp = a;
a = b;
b = tmp;
}
int main() {
int a=4, b=5;
myswap(a, b); // myswap(int&amp;amp; a, int&amp;amp; b) 호출
cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; '\t' &amp;lt;&amp;lt; b &amp;lt;&amp;lt; endl;
double c=0.3, d=12.5;
myswap(c, d); // myswap(double&amp;amp; a, double&amp;amp; b) 호출
cout &amp;lt;&amp;lt; c &amp;lt;&amp;lt; ' '\t' &amp;lt;&amp;lt; d &amp;lt;&amp;lt; endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 함수를 보면 매개 변수만 다르고 나머지 코드는 동일하다. 그럴 때 템플릿을 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1758890360804&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template &amp;lt;class T&amp;gt;
void myswap (T &amp;amp; a, T &amp;amp; b) {
T tmp;
tmp = a;
a = b;
b = tmp;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;C++ 템플릿(Template): 마법 같은 코드 재사용의 비밀  &amp;zwj;♂️&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++ 프로그래밍을 하다 보면 vector&amp;lt;int&amp;gt;, list&amp;lt;string&amp;gt;처럼 꺾쇠괄호와 함께 쓰이는 코드를 자주 만나게 됩니다. 바로 이것이 C++의 강력한 기능, **템플릿(Template)**입니다. 템플릿은 마치 '만능 틀'과 같아서, 자료형에 구애받지 않는 유연하고 재사용성 높은 코드를 작성하게 해주는 핵심 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 C++ 템플릿의 장점과 단점, 그리고 그 기반이 되는 '제네릭 프로그래밍'에 대해 알아보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✨ 템플릿의 강력한 장점&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 압도적인 코드 재사용성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿의 가장 큰 장점은 &lt;b&gt;코드의 재사용&lt;/b&gt;입니다. 정수(int)를 더하는 함수, 실수(double)를 더하는 함수를 따로 만들 필요 없이, 템플릿으로 add() 함수 하나만 만들어두면 어떤 숫자 타입이든 처리할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;하나의 코드로 모든 자료형을 다룬다!&lt;/b&gt; 이것이 템플릿의 핵심입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 뛰어난 생산성과 유용성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 반복해서 작성할 필요가 없으니 버그가 줄어들고 개발 시간은 단축됩니다. 이는 곧바로 &lt;b&gt;소프트웨어의 전체적인 생산성 향상&lt;/b&gt;으로 이어집니다. 잘 만들어진 템플릿 하나는 여러 프로젝트에서 유용하게 사용될 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  알아두어야 할 템플릿의 단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 템플릿에도 몇 가지 단점은 존재합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 낮은 포팅(Porting) 가능성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿은 비교적 현대적인 C++ 기능이므로, 오래된 컴파일러나 특정 시스템의 컴파일러에서는 완벽하게 지원하지 않을 수 있습니다. 다른 환경으로 코드를 이식(porting)할 때 예상치 못한 문제에 부딪힐 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 불친절한 컴파일 오류 메시지&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿을 사용하다가 오류가 발생하면, 수십 수백 줄에 달하는 길고 암호 같은 오류 메시지를 마주하게 될 수 있습니다. 이는 &lt;b&gt;디버깅을 매우 어렵게 만드는 요인&lt;/b&gt; 중 하나로, 특히 초보 개발자에게는 큰 장벽이 되기도 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  제네릭 프로그래밍 (Generic Programming) 이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿의 개념을 이해하기 위해선 **제네릭 프로그래밍(Generic Programming)**을 알아야 합니다. '일반화 프로그래밍'이라고도 불리는 이 기법은, 특정 자료형에 의존하지 않고 모든 종류의 자료형에 대해 동작하는 일반화된 코드(제네릭 함수, 제네릭 클래스)를 작성하는 것을 목표로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++에서는 &lt;b&gt;템플릿&lt;/b&gt;이 바로 이 제네릭 프로그래밍을 구현하는 도구입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;STL (Standard Template Library)&lt;/b&gt; C++의 표준 라이브러리인 STL은 제네릭 프로그래밍의 결정체입니다. vector, map, list, algorithm 등 우리가 편리하게 사용하는 모든 것들이 템플릿으로 만들어져 있습니다. 덕분에 우리는 vector&amp;lt;int&amp;gt;, vector&amp;lt;string&amp;gt;, vector&amp;lt;MyClass&amp;gt;처럼 어떤 자료형이든 담을 수 있는 동적 배열을 손쉽게 사용할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  제네릭 프로그래밍의 보편화 추세&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 제네릭 프로그래밍의 강력함은 C++에만 국한되지 않습니다. &lt;b&gt;Java의 제네릭스(Generics)&lt;/b&gt;, &lt;b&gt;C#의 제네릭(Generic)&lt;/b&gt; 등 현대적인 프로그래밍 언어 대부분이 이 개념을 적극적으로 채택하여 활용하고 있습니다. 그만큼 소프트웨어 개발에서 '일반화'와 '재사용성'이 중요한 가치임을 보여주는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿은 처음엔 다소 복잡하게 느껴질 수 있지만, 한번 익숙해지면 C++ 프로그래밍의 수준을 한 단계 끌어올려 주는 강력한 무기가 될 것입니다&lt;/p&gt;</description>
      <category>C++</category>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/5</guid>
      <comments>https://jae-oan.tistory.com/5#entry5comment</comments>
      <pubDate>Fri, 26 Sep 2025 21:43:31 +0900</pubDate>
    </item>
    <item>
      <title>React  위한 개발환경 세팅과 문법 정리</title>
      <link>https://jae-oan.tistory.com/4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;1. React 개발환경 세팅 (with Vite)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;React를 시작하기 전에 먼저 개발 환경부터 준비해야 합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;최근엔 &lt;b&gt;Vite&lt;/b&gt;가 &lt;code&gt;CRA(create-react-app)&lt;/code&gt;보다 훨씬 빠르고 효율적이라 많이 사용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;✅ Node.js 설치&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;공식 웹사이트: &lt;a href=&quot;https://nodejs.org&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nodejs.org&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;설치하면 &lt;code&gt;npm&lt;/code&gt;도 함께 설치됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;npm:&lt;/b&gt; 자바스크립트 패키지(라이브러리)를 관리해주는 도구입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;✅ 프로젝트 생성 순서&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 1. 새 폴더 만들기
mkdir my-react-blog

# 2. 폴더로 이동
cd my-react-blog

# 3. Vite로 React 프로젝트 생성
npm create vite@latest

# 4. 프로젝트 이름 입력 (예: my-react-app)
# 5. 프레임워크 선택 &amp;rarr; React
# 6. Variant 선택 &amp;rarr; JavaScript

# 7. 프로젝트 폴더로 이동
cd my-react-app

# 8. 패키지 설치
npm install

# 9. 개발 서버 실행
npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;  브라우저에서 &lt;code&gt;http://localhost:5173&lt;/code&gt; 에 접속하면 React 개발화면이 나타납니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt; ️ 2. 기본 파일 구조&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;my-react-app/
├── public/
├── src/
│   ├── App.jsx     &amp;larr; 핵심 컴포넌트
│   ├── main.jsx    &amp;larr; 앱의 시작점
│   └── index.css
├── index.html      &amp;larr; 진짜 HTML 파일 (root 엘리먼트)
├── package.json    &amp;larr; 명령어와 라이브러리 정보
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;✅ 실질적으로 작업하는 공간은 대부분 &lt;code&gt;App.jsx&lt;/code&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;  3. JSX 기초 문법 정리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;JSX는 자바스크립트 안에서 HTML을 작성할 수 있게 해주는 문법입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;✅ class는 className으로 써야 함&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;h4 className=&quot;title&quot;&amp;gt;블로그 제목&amp;lt;/h4&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;✅ 자바스크립트 변수는 중괄호 {} 사용&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;let post = &quot;강남 우동 맛집&quot;;
&amp;lt;h4&amp;gt;{post}&amp;lt;/h4&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;✅ 스타일은 객체처럼 작성&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;&amp;lt;h4 style={{ color: 'red', fontSize: '20px' }}&amp;gt;제목&amp;lt;/h4&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;✅ HTML은 반드시 return() 안에 작성&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h4&amp;gt;블로그&amp;lt;/h4&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;  4. useState &amp;ndash; 상태 관리의 시작&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;import { useState } from 'react';

let [likes, setLikes] = useState(0);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;code&gt;likes&lt;/code&gt;: 현재 상태값&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;code&gt;setLikes&lt;/code&gt;: 상태를 변경하는 함수&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;✅ 상태 출력&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;span&amp;gt;{likes}&amp;lt;/span&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;✅ 상태 변경&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;button onClick={() =&amp;gt; setLikes(likes + 1)}&amp;gt;  좋아요&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;nbsp;배열 상태 변경 시 복사본 사용&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;let [titles, setTitles] = useState(['강남 우동 맛집', '남자 코트 추천']);

let copy = [...titles];
copy[0] = '여자 코트 추천';
setTitles(copy);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;  5. 반복되는 HTML 처리 &amp;ndash; map()&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;nbsp;배열 map()으로 렌더링&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;let posts = ['강남 우동', '코트 추천', '파이썬 독학'];

return (
  &amp;lt;div&amp;gt;
    {posts.map((title, i) =&amp;gt; (
      &amp;lt;h4 key={i}&amp;gt;{title}&amp;lt;/h4&amp;gt;
    ))}
  &amp;lt;/div&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;⚠️ JSX 안에서는 &lt;code&gt;for&lt;/code&gt; 대신 &lt;code&gt;map()&lt;/code&gt;을 사용해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;  6. 동적 UI 설계 패턴 (3단계)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;동적 UI를 만들 때는 &lt;b&gt;디자인 &amp;rarr; 상태 &amp;rarr; 조건부 렌더링&lt;/b&gt; 순서로 진행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;Step 1. 기본 UI&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;button&amp;gt;설명 열기&amp;lt;/button&amp;gt;
&amp;lt;p&amp;gt;이건 설명입니다.&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;Step 2. 상태 변수 생성&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;let [showDesc, setShowDesc] = useState(false);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;Step 3. 조건부 렌더링&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;button onClick={() =&amp;gt; setShowDesc(!showDesc)}&amp;gt;설명 열기&amp;lt;/button&amp;gt;
{showDesc ? &amp;lt;p&amp;gt;이건 설명입니다.&amp;lt;/p&amp;gt; : null}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;8. package.json에서 명령어 수정하기&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
  &quot;dev&quot;: &quot;vite&quot;,
  &quot;build&quot;: &quot;vite build&quot;,
  &quot;preview&quot;: &quot;vite preview&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;예를 들어 &lt;code&gt;npm start&lt;/code&gt;로 실행하고 싶다면:&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
  &quot;start&quot;: &quot;vite&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;nbsp;9. 디스트럭쳐링 (Destructuring)&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;let [a, b] = [1, 2];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;rarr; &lt;code&gt;useState()&lt;/code&gt;에서 &lt;code&gt;[값, 변경함수]&lt;/code&gt; 구조로 사용하는 것도 같은 원리입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;마무리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;React 입문자를 위한 개발환경 설정부터 JSX 문법, 상태관리, 이벤트 처리, 컴포넌트 구성까지&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;React의 기본을 한 번에 정리해봤습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/h2&gt;</description>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/4</guid>
      <comments>https://jae-oan.tistory.com/4#entry4comment</comments>
      <pubDate>Thu, 24 Jul 2025 16:12:51 +0900</pubDate>
    </item>
    <item>
      <title>☁️구름톤 자바스크립트 기초 정리</title>
      <link>https://jae-oan.tistory.com/3</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  자바스크립트 기초 개념 &amp;amp; 실습 정리&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 1. 변수 (Variable)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  개념&lt;/b&gt;&lt;br /&gt;값을 저장할 공간을 만드는 것&lt;br /&gt;&lt;code&gt;let&lt;/code&gt;: 값을 나중에 바꿀 수 있음&lt;br /&gt;&lt;code&gt;const&lt;/code&gt;: 한 번만 값을 지정 (변경 불가능)&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;let name = &quot;Mike&quot;;
const AGE = 30;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 2. 자료형 (Data Type)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  개념&lt;/b&gt;&lt;br /&gt;자바스크립트의 기본 자료형: 문자열, 숫자, 불린, 객체, 배열, undefined/null&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;const name = &quot;Jane&quot;;
const age = 20;
const isAdult = true;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 3. 문자열 다루기 &amp;amp; 템플릿 리터럴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  개념&lt;/b&gt;&lt;br /&gt;백틱(&lt;code&gt;`&lt;/code&gt;)과 &lt;code&gt;${변수}&lt;/code&gt; 사용&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const name = &quot;Tom&quot;;
const message = `Hello, my name is ${name}`;
console.log(message); // Hello, my name is Tom
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 4. 사용자 입력 (prompt, alert, confirm)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  개념&lt;/b&gt;&lt;br /&gt;사용자로부터 입력 받기&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;// const name = prompt(&quot;이름을 입력하세요&quot;);
// alert(`안녕하세요 ${name}님!`);
// const isAdult = confirm(&quot;성인이신가요?&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 5. 형변환 (Type Conversion)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  개념&lt;/b&gt;&lt;br /&gt;prompt는 문자형으로 입력됨 &amp;rarr; 숫자로 변환 필요&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;const math = prompt(&quot;수학 점수&quot;);
const eng = prompt(&quot;영어 점수&quot;);
const avg = (Number(math) + Number(eng)) / 2;
console.log(avg);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 6. 연산자&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;let num = 2 ** 3;
console.log(num); // 8

let x = 10;
x += 5; // 15

let y = 5;
console.log(++y); // 6
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 7. 조건문 (if / else)&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;let age = 19;
if (age &amp;gt; 19) {
  console.log(&quot;성인입니다&quot;);
} else if (age === 19) {
  console.log(&quot;19살이네요!&quot;);
} else {
  console.log(&quot;미성년자입니다&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 8. 객체 (Object)&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;const person = {
  name: &quot;Clark&quot;,
  age: 30
};
person.job = &quot;hero&quot;;
delete person.age;
console.log(person);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 9. 객체 생성 함수&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function makeUser(name, age) {
  return {
    name,
    age,
    hobby: &quot;reading&quot;
  };
}
const user1 = makeUser(&quot;Alice&quot;, 25);
console.log(user1);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 10. 객체 속성 확인&lt;/h3&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;console.log(&quot;age&quot; in user1); // true
console.log(&quot;birth&quot; in user1); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 11. 함수로 조건 확인&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function isAdult(user) {
  if (user.age &amp;lt; 20) return false;
  return true;
}
const jane = { name: &quot;Jane&quot; };
console.log(isAdult(jane)); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 12. 배열과 반복문&lt;/h3&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;let days = [&quot;mon&quot;, &quot;tue&quot;, &quot;wed&quot;];
for (let i = 0; i &amp;lt; days.length; i++) {
  console.log(days[i]);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  마무리 요약표&lt;/h3&gt;
&lt;table border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;5&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;주제&lt;/th&gt;
&lt;th&gt;핵심 요약 내용&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;변수&lt;/td&gt;
&lt;td&gt;&lt;code&gt;let&lt;/code&gt;, &lt;code&gt;const&lt;/code&gt; 구분해서 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자료형&lt;/td&gt;
&lt;td&gt;문자열, 숫자, 불린, 객체, 배열 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;입력/출력&lt;/td&gt;
&lt;td&gt;&lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;alert&lt;/code&gt;, &lt;code&gt;confirm&lt;/code&gt; 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;형변환&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number()&lt;/code&gt;, &lt;code&gt;String()&lt;/code&gt;, &lt;code&gt;Boolean()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;연산자&lt;/td&gt;
&lt;td&gt;산술, 비교, 증가/감소, 제곱 연산자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;조건문&lt;/td&gt;
&lt;td&gt;&lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else if&lt;/code&gt;, &lt;code&gt;===&lt;/code&gt; 추천&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;객체&lt;/td&gt;
&lt;td&gt;관련 정보 묶는 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;배열&lt;/td&gt;
&lt;td&gt;순서 있는 값들, 반복문과 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  자바스크립트 함수 선언식 &amp;rarr; 표현식 변환 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서는 함수를 작성하는 여러 방식이 있습니다. 이 포스팅에서는 &lt;b&gt;함수 선언식&lt;/b&gt;을 &lt;b&gt;함수 표현식&lt;/b&gt;으로, 그리고 &lt;b&gt;화살표 함수&lt;/b&gt;로 점진적으로 변환하는 과정을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1단계: 함수 선언식 (Function Declaration)&lt;/h3&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;function greet(name) {
  return `Hello, ${name}!`;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✔️ 특징:&lt;/b&gt; 함수 선언식은 코드 어디서든 호출 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2단계: 함수 표현식 (Function Expression)&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const greet = function(name) {
  return `Hello, ${name}!`;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위를 코드를 아래와 같이 표현할 수 있습니다&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3단계: 화살표 함수 (Arrow Function)&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const greet = (name) =&amp;gt; {
  return `Hello, ${name}!`;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 더 간단하게:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const greet = name =&amp;gt; `Hello, ${name}!`;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✔️ 특징:&lt;/b&gt; 가장 간결한 방식. &lt;code&gt;this&lt;/code&gt; 바인딩이 없음. 짧은 함수에서 자주 사용됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front_end</category>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/3</guid>
      <comments>https://jae-oan.tistory.com/3#entry3comment</comments>
      <pubDate>Sat, 3 May 2025 22:48:59 +0900</pubDate>
    </item>
    <item>
      <title>구름톤 ☁️ 자바스크립트,css 기초 개념정리</title>
      <link>https://jae-oan.tistory.com/2</link>
      <description>&lt;h1&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바스크립트와 CSS 기초 개념 정리  &lt;/span&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프론트엔드 개발을 시작하면서 꼭 알아야 할 &lt;b&gt;자바스크립트&lt;/b&gt;와 &lt;b&gt;CSS 레이아웃(Flex/Grid)&lt;/b&gt;, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 &lt;b&gt;Float&lt;/b&gt;에 대한 기초 개념을 간단한 예시와 함께 정리해봤습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  자바스크립트 기본 문법&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;document.getElementById('hello').innerHTML = '안녕';
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✔️ 코드해석&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;document&lt;/code&gt; : 문서 (웹 페이지 전체)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;getElementById&lt;/code&gt; : &lt;b&gt;id로 HTML 요소를 가져오는 선택자&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;innerHTML&lt;/code&gt; : &lt;b&gt;요소 내부의 내용 (텍스트나 HTML)&lt;/b&gt; 을 바꾼다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;=&lt;/code&gt; : 대입 연산자 (오른쪽 값을 왼쪽에 넣음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;'안녕'&lt;/code&gt; : 문자열은 작은따옴표(&lt;code&gt;'&lt;/code&gt;) 또는 큰따옴표(&lt;code&gt;&quot;&lt;/code&gt;)로 감싼다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  영어 뜻만 잘 파악하면, 코드가 무슨 일을 하는지 쉽게 이해할 수 있어요!&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  innerHTML 외에도 바꿀 수 있는 속성 (style)&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;document.getElementById('hello').style.color = 'red';
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 하면 텍스트 색상이 빨간색으로 바뀝니다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그 외에도 아래와 같은 스타일 속성들을 조작할 수 있어요:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;style.fontSize = '20px'&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;style.margin = '10px'&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;style.padding = '5px'&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  함수(Function)와 파라미터(Parameter)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바스크립트에서 함수를 사용하면 &lt;b&gt;코드를 재사용&lt;/b&gt;하고 &lt;b&gt;유연한 동작&lt;/b&gt;을 할 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;함수 기본 예제&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;function plus(a) {
  console.log(2 + a);
}

plus(1); // 3
plus(2); // 4
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;파라미터가 여러 개일 경우&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;function plus1(a, b, c) {
  console.log(2 + a + b + c);
}

plus1(1, 2, 3); // 8
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ &lt;code&gt;ctrl + /&lt;/code&gt; 또는 &lt;code&gt;ctrl + ?&lt;/code&gt;를 누르면 &lt;b&gt;한 줄 주석 처리&lt;/b&gt;를 할 수 있어요!&lt;/span&gt;&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  CSS 레이아웃&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  Flexbox vs Grid&lt;/span&gt;&lt;/h3&gt;
&lt;table border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;5&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구분&lt;/span&gt;&lt;/th&gt;
&lt;th&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Flexbox&lt;/span&gt;&lt;/th&gt;
&lt;th&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Grid&lt;/span&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배치방식&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1차원 (가로 or 세로)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2차원 (가로 + 세로)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;wrap 사용&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;wrap&lt;/code&gt;으로 2차원 느낌 가능&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본이 2차원&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용 예&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아이템 정렬 중심&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;페이지 전체 구조&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  Grid 예시&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 200px 100px 100px;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;설명&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;grid-template-columns&lt;/code&gt;: 열(column)의 너비를 지정 (3개의 열이 각각 100px)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;grid-template-rows&lt;/code&gt;: 행(row)의 높이를 지정 (100px, 200px, 100px, 100px)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lqamo/btsNjhNp8Qg/YYWuSVdwkykj8qbW86grg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lqamo/btsNjhNp8Qg/YYWuSVdwkykj8qbW86grg0/img.png&quot; data-alt=&quot;예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lqamo/btsNjhNp8Qg/YYWuSVdwkykj8qbW86grg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flqamo%2FbtsNjhNp8Qg%2FYYWuSVdwkykj8qbW86grg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;687&quot; height=&quot;381&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;반복 문법&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;grid-template-columns: repeat(3, 100px);
grid-template-rows: 100px 200px repeat(2, 100px);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;rarr; 반복 문법을 사용하면 &lt;b&gt;더 간결하게&lt;/b&gt; 코드를 작성할 수 있다!&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  Flexbox 속성 정리&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.container {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  align-content: space-between;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;flex-direction&lt;/code&gt;: 가로 or 세로 방향 설정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;flex-wrap&lt;/code&gt;: 줄바꿈 허용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;justify-content&lt;/code&gt;: 주축 정렬&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;align-items&lt;/code&gt;: 교차축 정렬 (개별 항목)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;align-content&lt;/code&gt;: 교차축 정렬 (전체 줄)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;flex-basis&lt;/code&gt;: 아이템의 기본 크기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  Float 개념 &amp;amp; 사용법&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  Float이란?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;code&gt;float&lt;/code&gt;는 &lt;b&gt;이미지나 텍스트를 &lt;/b&gt;어떻게 배치할 건지 나타내는 용도였다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;img {
  float: left;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  float의 문제점과 해결&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.clearfix::after {
  content: &quot;&quot;;
  display: block;
  clear: both;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또는 간단히 아래처럼 작성할 수 있어요:&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.clear {
  clear: both;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  색상 코드 추천 사이트&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://m2.material.io/resources/color/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Color Tool (Material Design)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;</description>
      <category>Front_end</category>
      <category>프론트엔드</category>
      <author>Ta_m</author>
      <guid isPermaLink="true">https://jae-oan.tistory.com/2</guid>
      <comments>https://jae-oan.tistory.com/2#entry2comment</comments>
      <pubDate>Sat, 12 Apr 2025 01:05:02 +0900</pubDate>
    </item>
  </channel>
</rss>