<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>프로그래밍 공부방</title>
    <link>https://qweasd5123.tistory.com/</link>
    <description>목표는 풀스택 개발자!</description>
    <language>ko</language>
    <pubDate>Sat, 9 May 2026 23:31:32 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>qweasd5123</managingEditor>
    <item>
      <title>[LostBoard 프로젝트] 더보기 효율 화면 수정 - 2. 컴포넌트 분리 및 인터렉티브 UI 수정</title>
      <link>https://qweasd5123.tistory.com/72</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  현재 상황 분석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1단계에서 탄탄한 데이터 구조를 구축한 후, &quot;거대한 컴포넌트를 어떻게 작은 단위로 나눌 것인가?&quot; 그리고 &quot;사용자가 원하는 인터랙티브한 UI를 어떻게 구현할 것인가?&quot; 라는 두 가지 핵심 문제를 해결해야 합니다.&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;b&gt;1. 동일한 코드가 반복되는 문제&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1759242792761&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Tier4.js - 기존 코드
function Tier4(props) {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Grid container spacing={{ xs: 2, md: 3 }}&amp;gt;
        {raidList.map((raidName, index) =&amp;gt; (
          &amp;lt;Grid key={index} size={{ xs: 2, sm: 4, md: 4 }}&amp;gt;
            &amp;lt;div className=&quot;efficiency-raid-card&quot;&amp;gt;
              &amp;lt;img src={...} className=&quot;efficiency-raid-image&quot; /&amp;gt;
              &amp;lt;div className=&quot;efficiency-raid-title&quot;&amp;gt;{raidName}&amp;lt;/div&amp;gt;
              &amp;lt;div className=&quot;efficiency-raid-info&quot;&amp;gt;
                {/* 복잡한 배열 처리 로직 */}
                {arr[index]?.map((a, i) =&amp;gt; (
                  &amp;lt;div key={i}&amp;gt;{a}&amp;lt;/div&amp;gt;
                ))}
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/Grid&amp;gt;
        ))}
      &amp;lt;/Grid&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

// Tier3.js - 거의 동일한 코드 반복&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;Tier4.js와 Tier3.js에서 90% 이상 동일한 코드&lt;/li&gt;
&lt;li&gt;새로운 기능 추가 시 두 파일 모두 수정 필요&lt;/li&gt;
&lt;li&gt;버그 수정도 두 번씩 해야&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.&amp;nbsp;복잡한&amp;nbsp;데이터&amp;nbsp;처리&amp;nbsp;로직&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;UI 컴포넌트에 데이터 처리 로직이 섞임&lt;/li&gt;
&lt;li&gt;특히 Tier3.js의 중첩 배열 처리가 매우 복잡&lt;/li&gt;
&lt;li&gt;가독성과&amp;nbsp;유지보수성&amp;nbsp;저하&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.&amp;nbsp;관심사&amp;nbsp;분리&amp;nbsp;부족&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;레이아웃, 데이터 처리, UI 렌더링이 한 곳에 섞임&lt;/li&gt;
&lt;li&gt;테스트하기 어려운 구조&lt;/li&gt;
&lt;li&gt;재사용성&amp;nbsp;제로&lt;/li&gt;
&lt;/ul&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;b&gt; 해결&amp;nbsp;방안:&amp;nbsp;컴포넌트&amp;nbsp;아키텍처&amp;nbsp;설계&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;설계&amp;nbsp;원칙&amp;nbsp;수립&lt;/b&gt;&lt;/h4&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;/li&gt;
&lt;li&gt;&lt;b&gt;재사용성:&lt;/b&gt; 4티어, 3티어에서 동일한 컴포넌트 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확장성:&lt;/b&gt; 새로운 기능 추가 시 최소한의 수정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관심사 분리:&lt;/b&gt; 데이터 처리와 UI 로직 완전 분리&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;새로운&amp;nbsp;컴포넌트&amp;nbsp;구조&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1759243169017&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;src/components/
├── RaidCard/
│   ├── RaidCard.js          # 메인 카드 컴포넌트
│   └── RaidCard.css         # 카드 전용 스타일
└── RaidGrid/
    ├── RaidGrid.js          # 그리드 레이아웃 컴포넌트
    └── RaidGrid.css         # 그리드 전용 스타일&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; &amp;nbsp;RaidCard&amp;nbsp;컴포넌트&amp;nbsp;구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;핵심&amp;nbsp;아이디어:&amp;nbsp;하이브리드&amp;nbsp;데이터&amp;nbsp;처리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기존&amp;nbsp;코드와의&amp;nbsp;호환성을&amp;nbsp;유지하면서&amp;nbsp;새로운&amp;nbsp;구조도&amp;nbsp;지원&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243247003&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * 메인 RaidCard 컴포넌트
 */
function RaidCard({ 
  raidName, 
  raidData, 
  isNewStructure = false,  // 구조 타입 구분
  onDetailClick = null 
}) {
  // 데이터 처리 (구조에 따라 다른 처리)
  const processedData = isNewStructure 
    ? processNewData(raidData, selectedDifficulty)
    : processLegacyData(raidData);

  const { summary, details, availableDifficulties } = processedData;
  
  // ... UI 렌더링
}&lt;/code&gt;&lt;/pre&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;b&gt;레거시&amp;nbsp;데이터&amp;nbsp;처리&amp;nbsp;함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기존&amp;nbsp;문자열&amp;nbsp;배열&amp;nbsp;형태의&amp;nbsp;데이터를&amp;nbsp;처리하는&amp;nbsp;함수:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243265829&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function processLegacyData(legacyArray) {
  if (!Array.isArray(legacyArray)) {
    return { summary: '데이터 없음', details: [] };
  }

  // 기본 정보 추출 (첫 번째 항목에서)
  const firstItem = legacyArray[0] || '';
  const goldMatch = firstItem.match(/더보기 골드: (\d+)G/);
  const rewardMatch = firstItem.match(/더보기 보상 골드: ([\d.]+)/);
  
  const totalGold = goldMatch ? parseInt(goldMatch[1]) : 0;
  const totalReward = rewardMatch ? parseFloat(rewardMatch[1]) : 0;
  const efficiency = Math.round(totalReward - totalGold);

  return {
    summary: {
      totalGold,
      totalReward,
      efficiency: efficiency,
      gateCount: legacyArray.length
    },
    details: legacyArray
  };
}&lt;/code&gt;&lt;/pre&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;b&gt;새로운&amp;nbsp;구조&amp;nbsp;데이터&amp;nbsp;처리&amp;nbsp;함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1단계에서&amp;nbsp;만든&amp;nbsp;구조화된&amp;nbsp;데이터를&amp;nbsp;처리:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243293768&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function processNewData(structuredData, selectedDifficulty = null) {
  if (!structuredData || !structuredData.difficulties) {
    return { summary: '데이터 없음', details: [] };
  }

  // 선택된 난이도가 있으면 사용, 없으면 기본 난이도 사용
  const targetDifficulty = selectedDifficulty || structuredData.defaultDifficulty;
  const difficultyData = structuredData.difficulties[targetDifficulty];

  // 요약 정보 계산
  const totalGold = difficultyData.gates.reduce((sum, gate) =&amp;gt; sum + gate.goldCost, 0);
  const totalReward = difficultyData.gates.reduce((sum, gate) =&amp;gt; sum + gate.totalMaterialPrice, 0);
  
  return {
    summary: {
      totalGold,
      totalReward,
      efficiency: difficultyData.overallEfficiency,
      gateCount: difficultyData.gates.length,
      difficulty: targetDifficulty,
      itemLevel: difficultyData.itemLevel
    },
    details: difficultyData.gates,
    availableDifficulties: structuredData.availableDifficulties
  };
}&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;UI 컴포넌트 구조화&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;카드&amp;nbsp;레이아웃&amp;nbsp;설계&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1759243317333&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return (
  &amp;lt;div className=&quot;raid-card&quot;&amp;gt;
    {/* 카드 헤더 */}
    &amp;lt;div className=&quot;raid-card__header&quot;&amp;gt;
      &amp;lt;img src={...} className=&quot;raid-card__image&quot; /&amp;gt;
      &amp;lt;div className=&quot;raid-card__title&quot;&amp;gt;{raidName}&amp;lt;/div&amp;gt;
      {/* 난이도 배지 */}
    &amp;lt;/div&amp;gt;

    {/* 카드 내용 */}
    &amp;lt;div className=&quot;raid-card__content&quot;&amp;gt;
      {/* 요약 정보 */}
      &amp;lt;div className=&quot;raid-card__summary&quot;&amp;gt;
        &amp;lt;div className=&quot;summary-item&quot;&amp;gt;
          &amp;lt;span className=&quot;summary-label&quot;&amp;gt;총 골드&amp;lt;/span&amp;gt;
          &amp;lt;span className=&quot;summary-value&quot;&amp;gt;{totalGold}G&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
        {/* ... 다른 요약 정보들 */}
      &amp;lt;/div&amp;gt;

      {/* 상세 정보 */}
      &amp;lt;div className=&quot;raid-card__details&quot;&amp;gt;
        {/* 관문별 상세 정보 */}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    {/* 카드 액션 */}
    &amp;lt;div className=&quot;raid-card__actions&quot;&amp;gt;
      {/* 버튼들 */}
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
);&lt;/code&gt;&lt;/pre&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;b&gt;RaidGrid&amp;nbsp;컴포넌트&amp;nbsp;구현&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;pre id=&quot;code_1759243405715&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return (
    &amp;lt;div className=&quot;raid-grid&quot;&amp;gt;
      {/* 그리드 헤더 : 업데이트된 시간 표시 */}
      &amp;lt;div className=&quot;raid-grid__header&quot;&amp;gt;
        &amp;lt;div className=&quot;stat-item&quot;&amp;gt;
          &amp;lt;span className=&quot;stat-label&quot;&amp;gt;데이터 업데이트&amp;lt;/span&amp;gt;
          &amp;lt;span className=&quot;stat-value&quot;&amp;gt;
            {new Date().toLocaleTimeString('ko-KR', {
              hour: '2-digit',
              minute: '2-digit'
            })}
          &amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;

      {/* 메인 그리드 */}
      &amp;lt;Grid
        container
        spacing={{ xs: 2, md: 3 }}
        columns={{ xs: 4, sm: 8, md: 12 }}
        className=&quot;raid-grid__container&quot;
      &amp;gt;
        {raidList.map((raidName, index) =&amp;gt; (
          &amp;lt;Grid
            key={`${raidName}-${index}`}
            size={{ xs: 4, sm: 4, md: 4 }}
            className=&quot;raid-grid__item&quot;
          &amp;gt;
            &amp;lt;RaidCard
              raidName={raidName}
              raidData={raidDataArray ? raidDataArray[index] : null}
              isNewStructure={isNewStructure}
              onDetailClick={onRaidDetailClick}
            /&amp;gt;
          &amp;lt;/Grid&amp;gt;
        ))}
      &amp;lt;/Grid&amp;gt;
     
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;난이도별&amp;nbsp;탭&amp;nbsp;시스템&amp;nbsp;구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용자&amp;nbsp;요구사항&amp;nbsp;분석&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&quot;하드&amp;nbsp;난이도만&amp;nbsp;보고&amp;nbsp;싶어요&quot; &lt;br /&gt;&quot;노말이랑&amp;nbsp;하드&amp;nbsp;효율성을&amp;nbsp;쉽게&amp;nbsp;비교하고&amp;nbsp;싶어요&quot; &lt;br /&gt;&quot;클릭&amp;nbsp;한&amp;nbsp;번으로&amp;nbsp;난이도를&amp;nbsp;바꿀&amp;nbsp;수&amp;nbsp;있으면&amp;nbsp;좋겠어요&quot;&lt;/b&gt;&lt;/span&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;b&gt;난이도&amp;nbsp;변경&amp;nbsp;기능&amp;nbsp;구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&amp;nbsp;상태&amp;nbsp;관리&amp;nbsp;추가&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243594189&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function RaidCard({ raidName, raidData, isNewStructure = false }) {
  // 난이도 변경을 위한 상태 추가
  const [selectedDifficulty, setSelectedDifficulty] = useState(null);

  // 난이도 변경 핸들러
  const handleDifficultyChange = (difficulty) =&amp;gt; {
    setSelectedDifficulty(difficulty);
    console.log(`${raidName} 난이도 변경: ${summary.difficulty} &amp;rarr; ${difficulty}`);
    
    //변경된 데이터 미리보기
    const newData = processNewData(raidData, difficulty);
    console.log(`순이익 변화: ${summary.efficiency}G &amp;rarr; ${newData.summary.efficiency}G`);
  };
}&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;&lt;b&gt;2.&amp;nbsp;클릭&amp;nbsp;가능한&amp;nbsp;난이도&amp;nbsp;탭&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243630566&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{/* 클릭 가능한 난이도 탭 표시 */}
{isNewStructure &amp;amp;&amp;amp; availableDifficulties &amp;amp;&amp;amp; availableDifficulties.length &amp;gt; 1 &amp;amp;&amp;amp; (
  &amp;lt;div className=&quot;raid-card__difficulties&quot;&amp;gt;
    &amp;lt;span className=&quot;difficulties-label&quot;&amp;gt;난이도 선택:&amp;lt;/span&amp;gt;
    &amp;lt;div className=&quot;difficulties-list&quot;&amp;gt;
      {availableDifficulties.map(diff =&amp;gt; (
        &amp;lt;button
          key={diff} 
          className={`difficulty-tag difficulty-tag--clickable ${
            diff === summary.difficulty ? 'difficulty-tag--active' : ''
          }`}
          onClick={() =&amp;gt; handleDifficultyChange(diff)}
          title={`${diff} 난이도로 변경`}
        &amp;gt;
          {diff.toUpperCase()}
        &amp;lt;/button&amp;gt;
      ))}
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
)}&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;&lt;b&gt;3.&amp;nbsp;시각적&amp;nbsp;피드백&amp;nbsp;강화&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243681932&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.difficulty-tag--clickable {
  cursor: pointer;
  padding: 6px 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  transition: all 0.2s ease;
}

.difficulty-tag--clickable:hover {
  background: var(--bg-tertiary);
  border-color: var(--primary-gold);
  color: var(--primary-gold);
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(212, 175, 55, 0.2);
}

.difficulty-tag--active {
  background: linear-gradient(135deg, var(--primary-gold), var(--primary-gold-light));
  color: var(--bg-primary);
  border-color: var(--primary-gold);
  box-shadow: 0 2px 8px rgba(212, 175, 55, 0.3);
}&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;br /&gt;&lt;b&gt; &amp;nbsp;기존&amp;nbsp;컴포넌트&amp;nbsp;리팩토링&lt;/b&gt; &lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Tier4.js&amp;nbsp;완전&amp;nbsp;개선&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Before&amp;nbsp;(97줄의&amp;nbsp;복잡한&amp;nbsp;코드)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243749795&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Tier4(props) {
  const raidList = ['카제로스(종막)', '아르모체(4막)', ...];
  let arr = calcEfficiency(raidList, props.itemData);

  if (!props.itemData) {
    return &amp;lt;div&amp;gt;로딩 중...&amp;lt;/div&amp;gt;;
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Grid container spacing={{ xs: 2, md: 3 }}&amp;gt;
        {raidList.map((raidName, index) =&amp;gt; (
          &amp;lt;Grid key={index} size={{ xs: 2, sm: 4, md: 4 }}&amp;gt;
            &amp;lt;div className=&quot;efficiency-raid-card&quot;&amp;gt;
              {/* 복잡한 UI 코드 30줄... */}
              &amp;lt;div className=&quot;efficiency-raid-info&quot;&amp;gt;
                {arr[index]?.map((a, i) =&amp;gt; (
                  &amp;lt;div key={i}&amp;gt;{a}&amp;lt;/div&amp;gt;
                ))}
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/Grid&amp;gt;
        ))}
      &amp;lt;/Grid&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&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;&lt;b&gt;After&amp;nbsp;(35줄의&amp;nbsp;깔끔한&amp;nbsp;코드)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243787670&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Tier4(props) {
  const raidList = ['카제로스(종막)', '아르모체(4막)', ...];
  
  // 새로운 데이터 구조 사용
  const useNewStructure = process.env.NODE_ENV === 'development' &amp;amp;&amp;amp; true;
  
  let raidDataArray = useNewStructure 
    ? hybridCalcEfficiency(raidList, props.itemData, true)
    : calcEfficiency(raidList, props.itemData);

  // 상세 정보 클릭 핸들러
  const handleRaidDetailClick = (raidName, raidData) =&amp;gt; {
    console.log(`${raidName} 상세 정보:`, raidData);
  };

  return (
    &amp;lt;div className=&quot;tier4-container&quot;&amp;gt;
      {/* 페이지 헤더 */}
      &amp;lt;div className=&quot;tier-header&quot;&amp;gt;
        &amp;lt;h2 className=&quot;tier-title&quot;&amp;gt;4티어 레이드 효율성&amp;lt;/h2&amp;gt;
        &amp;lt;p className=&quot;tier-description&quot;&amp;gt;
          4티어 레이드의 더보기 보상 효율성을 확인하세요
        &amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;

      {/* 새로운 RaidGrid 컴포넌트 사용 */}
      &amp;lt;RaidGrid
        raidList={raidList}
        raidDataArray={raidDataArray}
        isNewStructure={useNewStructure}
        onRaidDetailClick={handleRaidDetailClick}
        loading={!props.itemData}
        error={props.error || null}
      /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&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;b&gt;사용자&amp;nbsp;피드백&amp;nbsp;반영:&amp;nbsp;추가&amp;nbsp;개선사항&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1.&amp;nbsp;효율성&amp;nbsp;계산&amp;nbsp;방식&amp;nbsp;개선&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;사용자 피드백: &quot;백분율보다는 실제 골드를 보여주는게 더 알아보기 편할 것 같다.&quot;&lt;/b&gt;&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;&lt;b&gt;Before&amp;nbsp;(백분율&amp;nbsp;방식)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243890556&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;efficiency: (totalReward / totalGold * 100).toFixed(1) + &quot;%&quot;
// 결과: &quot;150.0%&quot;&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;&lt;b&gt;After&amp;nbsp;(실제&amp;nbsp;골드&amp;nbsp;차이)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243901171&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;efficiency: Math.round(totalReward - totalGold)
// 결과: &quot;+500G&quot; 또는 &quot;-200G&quot;&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;&lt;b&gt;개선&amp;nbsp;효과:&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;사용자가 &quot;아, 500골드 이익이구나!&quot; 바로 이해&lt;/li&gt;
&lt;li&gt;손실인 경우 &quot;-200G&quot;로 명확하게 표시&lt;/li&gt;
&lt;li&gt;레이드&amp;nbsp;선택&amp;nbsp;시&amp;nbsp;더&amp;nbsp;직관적인&amp;nbsp;판단&amp;nbsp;가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2.&amp;nbsp;상세&amp;nbsp;정보&amp;nbsp;기본&amp;nbsp;표시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;사용자&amp;nbsp;피드백:&amp;nbsp;&quot;매번&amp;nbsp;'상세히&amp;nbsp;보기'를&amp;nbsp;클릭하는게&amp;nbsp;번거로워요&quot;&lt;/span&gt;&lt;/b&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;&lt;b&gt;Before&amp;nbsp;(토글&amp;nbsp;방식)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759243967948&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. 카드 확인
2. &quot;상세히 보기&quot; 버튼 클릭
3. 관문별 정보 확인
4. 다른 카드로 이동
5. 다시 &quot;상세히 보기&quot; 클릭 (반복)&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;&lt;b&gt;After&amp;nbsp;(기본&amp;nbsp;표시)&lt;/b&gt; &lt;/p&gt;
&lt;pre id=&quot;code_1759244001904&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. 카드 확인
2. 관문별 정보 바로 확인 
3. 난이도 변경으로 즉시 비교
4. 다른 카드로 이동
5. 바로 정보 확인 가능&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;&lt;b&gt;구현:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759244021918&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 상세 정보 (항상 표시)
&amp;lt;div className=&quot;raid-card__details&quot;&amp;gt;
  {isNewStructure ? (
    details.map((gate, index) =&amp;gt; (
      &amp;lt;div key={index} className=&quot;gate-detail&quot;&amp;gt;
        &amp;lt;div className=&quot;gate-detail__header&quot;&amp;gt;
          &amp;lt;span className=&quot;gate-number&quot;&amp;gt;{gate.gate}관문&amp;lt;/span&amp;gt;
          &amp;lt;span className=&quot;gate-efficiency&quot;&amp;gt;
            {gate.efficiency &amp;gt;= 0 ? '+' : ''}{gate.efficiency.toLocaleString()}G
          &amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div className=&quot;gate-detail__content&quot;&amp;gt;
          &amp;lt;span&amp;gt;골드: {gate.goldCost.toLocaleString()}G&amp;lt;/span&amp;gt;
          &amp;lt;span&amp;gt;보상: {gate.totalMaterialPrice.toLocaleString()}G&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    ))
  ) : (
    // 기존 구조 처리
  )}
&amp;lt;/div&amp;gt;&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;b&gt; &amp;nbsp;핵심&amp;nbsp;학습&amp;nbsp;포인트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 컴포넌트 설계 원칙&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터&amp;nbsp;완벽할&amp;nbsp;필요는&amp;nbsp;없지만,&amp;nbsp;확장&amp;nbsp;가능한&amp;nbsp;구조를&amp;nbsp;고민해야&amp;nbsp;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단일&amp;nbsp;책임&amp;nbsp;원칙&amp;nbsp;적용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759244079074&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ❌ 잘못된 예: 하나의 컴포넌트가 모든 일을 함
function Tier4() {
  // 데이터 처리 + UI 렌더링 + 레이아웃 + 상태 관리
}

// ✅ 올바른 예: 각 컴포넌트가 하나의 역할만 담당
function Tier4() {
  return &amp;lt;RaidGrid /&amp;gt;; // 레이아웃만 담당
}

function RaidGrid() {
  return &amp;lt;RaidCard /&amp;gt;; // 그리드 배치만 담당
}

function RaidCard() {
  // 카드 UI만 담당
}&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;size18&quot;&gt;&lt;b&gt;2. 사용자 중심 설계&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를&amp;nbsp;작성하기&amp;nbsp;전에&amp;nbsp;사용자가&amp;nbsp;어떻게&amp;nbsp;사용할지&amp;nbsp;충분히&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;실제&amp;nbsp;사용&amp;nbsp;패턴&amp;nbsp;분석&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;li&gt;클릭&amp;nbsp;수를&amp;nbsp;최소화하고&amp;nbsp;싶어함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;피드백&amp;nbsp;반영&amp;nbsp;프로세스&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; &quot;백분율이 이해하기 어려워요&quot;&lt;/li&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;li&gt;&lt;b&gt;검증:&lt;/b&gt;&amp;nbsp;사용자&amp;nbsp;테스트&amp;nbsp;및&amp;nbsp;피드백&amp;nbsp;수집&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LostBoard 프로젝트</category>
      <category>lostboard 프로젝트</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/72</guid>
      <comments>https://qweasd5123.tistory.com/72#entry72comment</comments>
      <pubDate>Tue, 30 Sep 2025 23:57:12 +0900</pubDate>
    </item>
    <item>
      <title>[LostBoard 프로젝트] 더보기 효율 화면 수정 - 1. 복잡한 데이터 구조 수정</title>
      <link>https://qweasd5123.tistory.com/71</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt; &amp;nbsp;현재&amp;nbsp;상황&amp;nbsp;분석&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 Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;더보기 효율 화면은 플레이어들이 어떤 레이드의 &quot;더보기 보상&quot;의 효율을 한눈에 볼 수 있도록 도와주는 서비스입니다. 하지만&amp;nbsp;현재&amp;nbsp;효율성&amp;nbsp;분석&amp;nbsp;페이지에는&amp;nbsp;몇&amp;nbsp;가지&amp;nbsp;심각한&amp;nbsp;문제점들이&amp;nbsp;있었습니다&lt;/b&gt;&lt;/span&gt;&lt;/p&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;  발견된 문제점들&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1.&amp;nbsp;가독성이&amp;nbsp;너무&amp;nbsp;떨어진다 &lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&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;현재&amp;nbsp;화면에는&amp;nbsp;이런&amp;nbsp;식으로&amp;nbsp;정보가&amp;nbsp;표시됩니다:&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;931&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciQzg9/btsMfk6ENoH/z9RTsAFaKPhK0CuJjKdGOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciQzg9/btsMfk6ENoH/z9RTsAFaKPhK0CuJjKdGOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciQzg9/btsMfk6ENoH/z9RTsAFaKPhK0CuJjKdGOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciQzg9%2FbtsMfk6ENoH%2Fz9RTsAFaKPhK0CuJjKdGOk%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;1920&quot; height=&quot;931&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;931&quot;/&gt;&lt;/span&gt;&lt;/figure&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;사용자&amp;nbsp;입장에서는&amp;nbsp;&quot;어떤&amp;nbsp;보상을&amp;nbsp;얼마나&amp;nbsp;받는지&quot;,&amp;nbsp;&quot;효율성이&amp;nbsp;얼마나&amp;nbsp;되는지&quot;&amp;nbsp;직관적으로&amp;nbsp;파악하기&amp;nbsp;어려웠습니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2.&amp;nbsp;어떤&amp;nbsp;보상을&amp;nbsp;얼마만큼&amp;nbsp;얻었는지&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;없다&lt;/span&gt;&lt;/b&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;위의&amp;nbsp;텍스트에서는&amp;nbsp;&quot;더보기&amp;nbsp;보상&amp;nbsp;골드:&amp;nbsp;1234.5&quot;라고만&amp;nbsp;나오지,&amp;nbsp;실제로&amp;nbsp;어떤&amp;nbsp;재료를&amp;nbsp;몇&amp;nbsp;개씩&amp;nbsp;받는지&amp;nbsp;알&amp;nbsp;수&amp;nbsp;없었습니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3.&amp;nbsp;레이드별&amp;nbsp;특수&amp;nbsp;보상&amp;nbsp;획득량을&amp;nbsp;알&amp;nbsp;수&amp;nbsp;없다&lt;/span&gt;&lt;/b&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;Lost&amp;nbsp;Ark의&amp;nbsp;각&amp;nbsp;레이드는&amp;nbsp;고유한&amp;nbsp;특수&amp;nbsp;아이템들을&amp;nbsp;제공하는데,&amp;nbsp;이런&amp;nbsp;정보가&amp;nbsp;전혀&amp;nbsp;표시되지&amp;nbsp;않았습니다&lt;/span&gt;&lt;/p&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;기존&amp;nbsp;코드&amp;nbsp;분석&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존&amp;nbsp;코드의&amp;nbsp;구조&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1759154589377&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// calcEfficiency.js - 기존 코드
function calculateAdditionalReward(raid, itemData, reward) {
    return raid.additionalGold.map((gold, k) =&amp;gt; {
        let additionalRewardPrice = Object.keys(reward).reduce(
            (sum, key) =&amp;gt; sum + calcPrice(key, itemData, raid.additionalReward[k][Object.keys(reward).indexOf(key)]),
            0
        );
        //   문제: 모든 정보를 하나의 문자열로 반환
        return `${raid.RaidDifficulty} ${k + 1}관문 더보기 골드: ${gold}G 더보기 보상 골드: ${additionalRewardPrice}`;
    });
}

function calcEfficiency(raidList, itemData) {
    return raidList.map(raidName =&amp;gt; {
        let raids = Raid.filter(x =&amp;gt; x.RaidName === raidName);
        let reward = filteringItem(raids[0]?.RaidItemLevel || 0);
        
        //   문제: 모든 난이도가 배열에 섞여서 반환
        return raids.flatMap(raid =&amp;gt; calculateAdditionalReward(raid, itemData, reward));
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&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;b&gt;기존&amp;nbsp;구조의&amp;nbsp;문제점들&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1.&amp;nbsp;문자열&amp;nbsp;기반&amp;nbsp;데이터&amp;nbsp;반환&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759154645824&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 반환되는 데이터 예시
[
  [
    &quot;hard 1관문 더보기 골드: 2700G 더보기 보상 골드: 1234.5&quot;,
    &quot;hard 2관문 더보기 골드: 4100G 더보기 보상 골드: 2345.6&quot;, 
    &quot;normal 1관문 더보기 골드: 2400G 더보기 보상 골드: 987.3&quot;
  ]
]&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;size18&quot;&gt;&amp;nbsp;&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;UI에서 이 문자열을 파싱해야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;새로운 정보를 추가하려면 문자열 형식을 변경해야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정렬이나&amp;nbsp;필터링이&amp;nbsp;어려움&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2.&amp;nbsp;난이도별&amp;nbsp;구분&amp;nbsp;부족&lt;/span&gt;&lt;/b&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;모든&amp;nbsp;난이도(하드,&amp;nbsp;노말,&amp;nbsp;싱글)가&amp;nbsp;하나의&amp;nbsp;배열에&amp;nbsp;섞여&amp;nbsp;있어서,&amp;nbsp;UI에서&amp;nbsp;탭으로&amp;nbsp;나누기&amp;nbsp;어려웠습니다.&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;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;3.&amp;nbsp;특수&amp;nbsp;보상&amp;nbsp;정보&amp;nbsp;누락&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759154746711&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존 코드에서는 이 정보들이 무시됨
&quot;clearUniqueRewards&quot;: [
    { &quot;우뢰의 뇌옥&quot;: 3, &quot;특수 재련 : 순환 돌파석&quot;: 0 }
],
&quot;additionalUniqueRewards&quot;: [
    { &quot;우뢰의 뇌옥&quot;: 3, &quot;특수 재련 : 순환 돌파석&quot;: 0 }
]&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4.&amp;nbsp;복잡한&amp;nbsp;예외&amp;nbsp;처리&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759154766762&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 하드코딩된 예외 처리
const specialRaids = {
    '에키드나': [...],
    '카멘': [...],
    '아브렐슈드': [...]
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;해결&amp;nbsp;방안:&amp;nbsp;새로운&amp;nbsp;데이터&amp;nbsp;구조&amp;nbsp;설계&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;설계&amp;nbsp;원칙&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;구조화된 데이터:&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;b&gt;명확한 분리:&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;b&gt;완전한 정보:&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;b&gt;확장&amp;nbsp;가능성:&amp;nbsp;&lt;/b&gt;새로운&amp;nbsp;레이드&amp;nbsp;추가&amp;nbsp;시&amp;nbsp;코드&amp;nbsp;수정&amp;nbsp;불필요&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;핵심&amp;nbsp;개선사항&amp;nbsp;구현&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1.&amp;nbsp;구조화된&amp;nbsp;데이터&amp;nbsp;매핑&lt;/span&gt;&lt;/b&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;기존의&amp;nbsp;하드코딩된&amp;nbsp;구조를&amp;nbsp;개선했습니다:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759155051195&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// raidDataProcessor.js - 새로운 구조
const MATERIAL_MAPPING = {
    1640: { 
        destruction: '운명의 파괴석', 
        guardian: '운명의 수호석', 
        fragment: '운명의 파편', 
        breakthrough: '운명의 돌파석' 
    },
    1580: { 
        destruction: '정제된 파괴강석', 
        guardian: '정제된 수호강석', 
        fragment: '명예의 파편', 
        breakthrough: '찬란한 명예의 돌파석' 
    },
    // ... 다른 레벨들
};

function getMaterialTypes(raidItemLevel) {
    const levels = Object.keys(MATERIAL_MAPPING).map(Number).sort((a, b) =&amp;gt; b - a);
    const targetLevel = levels.find(level =&amp;gt; raidItemLevel &amp;gt;= level) || 0;
    return MATERIAL_MAPPING[targetLevel];
}&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;개선점:&lt;/b&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2.&amp;nbsp;관문별&amp;nbsp;상세&amp;nbsp;정보&amp;nbsp;구조화&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759155141185&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function processGateDetails(raidData, itemData, materialTypes) {
    return raidData.additionalGold.map((goldCost, gateIndex) =&amp;gt; {
        //   기본 재료 보상 계산
        const materials = {};
        const materialKeys = Object.keys(materialTypes);
        
        materialKeys.forEach((key, index) =&amp;gt; {
            const materialName = materialTypes[key];
            const quantity = raidData.additionalReward[gateIndex][index] || 0;
            materials[key] = {
                name: materialName,
                quantity: quantity,
                price: calculateMaterialPrice(materialName, itemData, quantity)
            };
        });

        //   특수 보상 처리 (기존에 누락되었던 부분!)
        const specialRewards = {};
        const uniqueRewards = raidData.additionalUniqueRewards[gateIndex] || {};
        
        Object.entries(uniqueRewards).forEach(([itemName, quantity]) =&amp;gt; {
            if (quantity &amp;gt; 0) {
                specialRewards[itemName] = {
                    name: itemName,
                    quantity: quantity,
                    price: 0 // 특수 아이템은 가격 정보가 없으므로 0
                };
            }
        });

        //   효율성 자동 계산
        const totalMaterialPrice = Object.values(materials).reduce(
            (sum, material) =&amp;gt; sum + material.price, 0
        );

        return {
            gate: gateIndex + 1,
            goldCost: goldCost,
            materials: materials,
            specialRewards: specialRewards,
            totalMaterialPrice: totalMaterialPrice,
            efficiency: totalMaterialPrice &amp;gt; 0 ? (totalMaterialPrice / goldCost * 100).toFixed(1) : 0
        };
    });
}&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;개선점:&lt;/b&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;효율성&amp;nbsp;자동&amp;nbsp;계산&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3.&amp;nbsp;난이도별&amp;nbsp;데이터&amp;nbsp;분리&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759155209831&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function getStructuredRaidData(raidName, itemData) {
    const raidVariants = Raid.filter(raid =&amp;gt; 
        raid.RaidName === raidName || 
        raid.RaidName.includes(raidName) ||
        raidName.includes(raid.RaidName.split('(')[0])
    );

    //   난이도별로 그룹화
    const difficulties = {};
    const availableDifficulties = [];

    raidVariants.forEach(raidData =&amp;gt; {
        const difficulty = raidData.RaidDifficulty;
        const materialTypes = getMaterialTypes(raidData.RaidItemLevel);
        
        difficulties[difficulty] = {
            itemLevel: raidData.RaidItemLevel,
            gates: processGateDetails(raidData, itemData, materialTypes),
            overallEfficiency: 0 // 나중에 계산
        };

        if (!availableDifficulties.includes(difficulty)) {
            availableDifficulties.push(difficulty);
        }
    });

    //   기본 난이도 설정 (우선순위: hard &amp;gt; normal &amp;gt; single)
    const difficultyPriority = ['hard', 'normal', 'single'];
    const defaultDifficulty = difficultyPriority.find(diff =&amp;gt; 
        availableDifficulties.includes(diff)
    ) || availableDifficulties[0];

    return {
        raidName,
        difficulties,
        availableDifficulties: availableDifficulties.sort((a, b) =&amp;gt; {
            const order = { 'hard': 1, 'normal': 2, 'single': 3 };
            return (order[a] || 999) - (order[b] || 999);
        }),
        defaultDifficulty
    };
}&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;개선점:&lt;/b&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;확장&amp;nbsp;가능한&amp;nbsp;구조&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;Before&amp;nbsp;vs&amp;nbsp;After&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;Before&amp;nbsp;(기존&amp;nbsp;구조)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1759155348526&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 반환되는 데이터
[
  [
    &quot;hard 1관문 더보기 골드: 2700G 더보기 보상 골드: 1234.5&quot;,
    &quot;hard 2관문 더보기 골드: 4100G 더보기 보상 골드: 2345.6&quot;, 
    &quot;hard 3관문 더보기 골드: 5800G 더보기 보상 골드: 3456.7&quot;,
    &quot;normal 1관문 더보기 골드: 2400G 더보기 보상 골드: 987.3&quot;
  ]
]&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;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;❌ UI 컴포넌트에서 활용 제한적&lt;/li&gt;
&lt;li&gt;❌&amp;nbsp;효율성&amp;nbsp;계산을&amp;nbsp;별도로&amp;nbsp;해야&amp;nbsp;함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  After (새로운 구조)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1759155398815&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 반환되는 데이터
[
  {
    raidName: &quot;모르둠(3막)&quot;,
    availableDifficulties: [&quot;hard&quot;, &quot;normal&quot;],
    defaultDifficulty: &quot;hard&quot;,
    difficulties: {
      hard: {
        itemLevel: 1700,
        overallEfficiency: &quot;142.3&quot;,
        gates: [
          {
            gate: 1,
            goldCost: 2700,
            totalMaterialPrice: 1234.5,
            efficiency: &quot;45.7&quot;,
            materials: {
              destruction: { 
                name: &quot;운명의 파괴석&quot;, 
                quantity: 830, 
                price: 128.65 
              },
              guardian: { 
                name: &quot;운명의 수호석&quot;, 
                quantity: 1660, 
                price: 136.12 
              },
              fragment: { 
                name: &quot;운명의 파편&quot;, 
                quantity: 7000, 
                price: 1.98 
              }
            },
            specialRewards: {
              &quot;우뢰의 뇌옥&quot;: { 
                name: &quot;우뢰의 뇌옥&quot;, 
                quantity: 3, 
                price: 0 
              }
            }
          }
          // ... 다른 관문들
        ]
      },
      normal: {
        // ... 노말 난이도 데이터
      }
    }
  }
]&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;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;✅ UI에서 바로 활용 가능&lt;/li&gt;
&lt;li&gt;✅ 효율성 계산 자동화&lt;/li&gt;
&lt;li&gt;✅&amp;nbsp;확장&amp;nbsp;가능한&amp;nbsp;구조&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;배운&amp;nbsp;점들&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1.&amp;nbsp;데이터&amp;nbsp;구조의&amp;nbsp;중요성&lt;/span&gt;&lt;/b&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;처음에는&amp;nbsp;&quot;일단&amp;nbsp;동작하게&amp;nbsp;만들자&quot;는&amp;nbsp;생각으로&amp;nbsp;문자열&amp;nbsp;기반으로&amp;nbsp;구현했지만,&amp;nbsp;기능이&amp;nbsp;복잡해질수록&amp;nbsp;한계가&amp;nbsp;드러났습니다.&amp;nbsp;처음부터&amp;nbsp;확장&amp;nbsp;가능한&amp;nbsp;구조를&amp;nbsp;고민하는&amp;nbsp;것이&amp;nbsp;중요하다는&amp;nbsp;것을&amp;nbsp;깨달았습니다.&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 사용자 관점에서 생각하기&lt;/span&gt;&lt;/b&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;개발자&amp;nbsp;입장에서는&amp;nbsp;&quot;동작하면&amp;nbsp;되지&quot;라고&amp;nbsp;생각할&amp;nbsp;수&amp;nbsp;있지만,&amp;nbsp;사용자가&amp;nbsp;실제로&amp;nbsp;어떤&amp;nbsp;정보를&amp;nbsp;원하는지&amp;nbsp;고민해야&amp;nbsp;합니다.&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 테스트의 중요성&lt;/span&gt;&lt;/b&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;새로운&amp;nbsp;구조를&amp;nbsp;만들&amp;nbsp;때&amp;nbsp;테스트&amp;nbsp;코드를&amp;nbsp;함께&amp;nbsp;작성하면&amp;nbsp;검증과&amp;nbsp;디버깅이&amp;nbsp;훨씬&amp;nbsp;쉬워집니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>LostBoard 프로젝트</category>
      <category>lostboard 프로젝트</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/71</guid>
      <comments>https://qweasd5123.tistory.com/71#entry71comment</comments>
      <pubDate>Mon, 29 Sep 2025 23:18:25 +0900</pubDate>
    </item>
    <item>
      <title>[LostBoard 프로젝트] 현황판 화면 디자인 수정</title>
      <link>https://qweasd5123.tistory.com/70</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존 현황판 화면&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPOEqb/btsMfqZX60v/0yBVVMlf7PnqHHuisQ9OR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPOEqb/btsMfqZX60v/0yBVVMlf7PnqHHuisQ9OR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPOEqb/btsMfqZX60v/0yBVVMlf7PnqHHuisQ9OR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPOEqb%2FbtsMfqZX60v%2F0yBVVMlf7PnqHHuisQ9OR0%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;1920&quot; height=&quot;835&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;835&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp; 문제점 분석&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;현황판 화면은 LostBoard의 핵심 페이지이지만, 여러 문제점들이 있었습니다.&lt;/span&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 시각적 가독성 문제&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1758383010768&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존 코드 - 단순한 이미지 표시
&amp;lt;img src={`${process.env.PUBLIC_URL}/class_logo/${characterInfo.CharacterClassName}.png`}
  width=&quot;30%&quot; alt={characterInfo.CharacterClassName} /&amp;gt;&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;캐릭터&amp;nbsp;정보가&amp;nbsp;평면적으로&amp;nbsp;나열되어&amp;nbsp;중요도&amp;nbsp;파악&amp;nbsp;어려움&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐릭터 간 구분이 어려움&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 정보 과부하&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시각적&amp;nbsp;계층&amp;nbsp;구조&amp;nbsp;부족&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;b&gt;3. 구식 UI 요소&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1758383107772&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존 체크박스 - 밋밋한 디자인
&amp;lt;Checkbox
  checked={checkedValues[characterIndex]?.[raidIndex] || false}
  onChange={(e) =&amp;gt; handleCheckboxChange(e, characterIndex, raidIndex)}
/&amp;gt;
&amp;lt;span&amp;gt;{raidName}&amp;lt;/span&amp;gt;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상태&amp;nbsp;변화에&amp;nbsp;대한&amp;nbsp;시각적&amp;nbsp;피드백&amp;nbsp;부족&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. 코드 복잡성&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;현황판 페이지 코드가 300줄 이상으로 유지보수 어려움&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모든 로직이 한 파일에 집중&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;재사용&amp;nbsp;가능한&amp;nbsp;컴포넌트&amp;nbsp;부족&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;단계별&amp;nbsp;개선&amp;nbsp;과정&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1단계: 캐릭터 카드 컴포넌트 분리&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선 목표&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아이템&amp;nbsp;레벨별&amp;nbsp;시각적&amp;nbsp;구분&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;새로운 CharacterCard 컴포넌트&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758383293923&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const CharacterAvatar = styled(Avatar)(({ theme }) =&amp;gt; ({
  width: 80,
  height: 80,
  border: '3px solid var(--primary-gold)',
  boxShadow: '0 0 20px var(--primary-gold-glow)',
  background: 'var(--bg-tertiary)',
  
  '&amp;amp; img': {
    filter: 'drop-shadow(0 0 10px rgba(212, 175, 55, 0.3))',
  }
}));

const ItemLevelChip = styled(Chip)(({ itemlevel }) =&amp;gt; {
  const getColorByLevel = (level) =&amp;gt; {
    if (level &amp;gt;= 1700) return { bg: '#FF6B6B', color: '#FFF' };
    if (level &amp;gt;= 1640) return { bg: '#4ECDC4', color: '#FFF' };
    if (level &amp;gt;= 1580) return { bg: '#45B7D1', color: '#FFF' };
    if (level &amp;gt;= 1490) return { bg: '#96CEB4', color: '#FFF' };
    return { bg: '#FFEAA7', color: '#2D3436' };
  };
  // ...
});&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선 효과:&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅&amp;nbsp;카드&amp;nbsp;형태로&amp;nbsp;캐릭터별&amp;nbsp;명확한&amp;nbsp;구분&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2단계:&amp;nbsp;인터랙티브&amp;nbsp;레이드&amp;nbsp;아이템&amp;nbsp;컴포넌트&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선 목표&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자&amp;nbsp;경험&amp;nbsp;개선&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;새로운 RaidItem 컴포넌트&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758383534203&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const RaidContainer = styled(Box)(({ ischecked, isgoldselected }) =&amp;gt; ({
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'space-between',
  padding: '16px 20px',
  background: ischecked === 'true' 
    ? 'linear-gradient(135deg, var(--bg-tertiary), var(--bg-secondary))' 
    : 'var(--bg-tertiary)',
  border: `2px solid ${
    ischecked === 'true' 
      ? 'var(--primary-gold)' 
      : 'var(--border-color)'
  }`,
  borderRadius: '12px',
  transition: 'all 0.3s ease',
  
  '&amp;amp;:hover': {
    borderColor: 'var(--primary-gold)',
    transform: 'translateX(4px)',
    boxShadow: '0 4px 20px rgba(212, 175, 55, 0.2)',
  },
}));&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인터렉티브 버튼&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758383567208&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 클리어 상태 토글
&amp;lt;IconButton onClick={onRaidToggle}&amp;gt;
  {isChecked ? &amp;lt;CheckCircleIcon /&amp;gt; : &amp;lt;RadioButtonUncheckedIcon /&amp;gt;}
&amp;lt;/IconButton&amp;gt;

// 골드 획득 버튼
&amp;lt;GoldButton isselected={isGoldSelected.toString()} disabled={isGoldDisabled}&amp;gt;
  &amp;lt;PaidIcon /&amp;gt;
&amp;lt;/GoldButton&amp;gt;

// 더보기 보상 버튼  
&amp;lt;GoldButton isselected={isAdditionalSelected.toString()} disabled={isAdditionalDisabled}&amp;gt;
  &amp;lt;AddIcon /&amp;gt;
&amp;lt;/GoldButton&amp;gt;&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선 효과:&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ 체크박스 &amp;rarr; 직관적인 아이콘 버튼으로 변경&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅&amp;nbsp;툴팁으로&amp;nbsp;기능&amp;nbsp;설명&amp;nbsp;추가&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3단계:&amp;nbsp;Board.js&amp;nbsp;리팩토링&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선 목표&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;재사용&amp;nbsp;가능한&amp;nbsp;구조로&amp;nbsp;변경&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존 복잡한 구조&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758383701000&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존 - 모든 로직이 한 곳에
return (
  &amp;lt;div className='content'&amp;gt;
    {sortedData.map((characterInfo, characterIndex) =&amp;gt; (
      &amp;lt;Grid container spacing={3} key={characterIndex}&amp;gt;
        &amp;lt;Grid size=&quot;grow&quot;&amp;gt;
          {/* 캐릭터 정보 */}
        &amp;lt;/Grid&amp;gt;
        &amp;lt;Grid size=&quot;grow&quot;&amp;gt;
          {/* 복잡한 레이드 목록 로직 */}
          {filteredRaidName[characterIndex].map((raidName, raidIndex) =&amp;gt; {
            // 50줄 이상의 복잡한 로직...
          })}
        &amp;lt;/Grid&amp;gt;
        &amp;lt;Grid size=&quot;grow&quot;&amp;gt;
          {/* 재료 정보 */}
        &amp;lt;/Grid&amp;gt;
      &amp;lt;/Grid&amp;gt;
    ))}
  &amp;lt;/div&amp;gt;
);&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선된 구조&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758383718970&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 개선 후 - 컴포넌트 분리로 깔끔하게
return (
  &amp;lt;div className='content'&amp;gt;
    &amp;lt;Container maxWidth=&quot;lg&quot;&amp;gt;
      {sortedData.map((characterInfo, characterIndex) =&amp;gt; (
        &amp;lt;Fade in={true} timeout={300 + characterIndex * 100} key={characterIndex}&amp;gt;
          &amp;lt;CharacterCard characterInfo={characterInfo}&amp;gt;
            {filteredRaidName[characterIndex]?.map((raidName, raidIndex) =&amp;gt; (
              &amp;lt;RaidItem
                key={raidIndex}
                raidName={raidName}
                isChecked={checkedValues[characterIndex]?.[raidIndex] || false}
                // ... 기타 props
                onRaidToggle={(e) =&amp;gt; handleCheckboxChange(e, characterIndex, raidIndex)}
              /&amp;gt;
            ))}
            &amp;lt;Material /* ... props */ /&amp;gt;
          &amp;lt;/CharacterCard&amp;gt;
        &amp;lt;/Fade&amp;gt;
      ))}
    &amp;lt;/Container&amp;gt;
  &amp;lt;/div&amp;gt;
);&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선 효과:&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ 코드 라인 수 50% 감소&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅&amp;nbsp;유지보수성&amp;nbsp;대폭&amp;nbsp;향상&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4단계:&amp;nbsp;시각적&amp;nbsp;개선&amp;nbsp;및&amp;nbsp;애니메이션&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;페이드 인 애니메이션&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758383819862&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Fade in={true} timeout={300 + characterIndex * 100} key={characterIndex}&amp;gt;
  &amp;lt;CharacterCard&amp;gt;
    {/* 컨텐츠 */}
  &amp;lt;/CharacterCard&amp;gt;
&amp;lt;/Fade&amp;gt;&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;호버 효과&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758383843068&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.character-card:hover {
  border-color: var(--primary-gold);
  box-shadow: var(--shadow-gold);
  transform: translateY(-4px);
}

.raid-item:hover {
  transform: translateX(4px);
  box-shadow: 0 4px 20px rgba(212, 175, 55, 0.2);
}&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt; &amp;nbsp;개선&amp;nbsp;결과&amp;nbsp;비교&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;b&gt;Before&amp;nbsp;(기존)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPOEqb/btsMfqZX60v/0yBVVMlf7PnqHHuisQ9OR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPOEqb/btsMfqZX60v/0yBVVMlf7PnqHHuisQ9OR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPOEqb/btsMfqZX60v/0yBVVMlf7PnqHHuisQ9OR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPOEqb%2FbtsMfqZX60v%2F0yBVVMlf7PnqHHuisQ9OR0%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;1920&quot; height=&quot;835&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;835&quot;/&gt;&lt;/span&gt;&lt;/figure&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  밋밋한 체크박스 UI&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  300줄의 복잡한 코드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;반응성&amp;nbsp;부족&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;b&gt;After&amp;nbsp;(개선&amp;nbsp;후)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/94EZ6/btsQI3LpVOG/SMwyv9AVtoeVP5g0ZJKFd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/94EZ6/btsQI3LpVOG/SMwyv9AVtoeVP5g0ZJKFd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/94EZ6/btsQI3LpVOG/SMwyv9AVtoeVP5g0ZJKFd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F94EZ6%2FbtsQI3LpVOG%2FSMwyv9AVtoeVP5g0ZJKFd1%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;1906&quot; height=&quot;954&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;954&quot;/&gt;&lt;/span&gt;&lt;/figure&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ 카드 기반 구조화된 정보&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ 인터랙티브한 버튼 UI&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ 150줄의 깔끔한 코드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅&amp;nbsp;부드러운&amp;nbsp;애니메이션&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;배운&amp;nbsp;점&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이번 디자인 개선을 통해 깨달은 것들&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 사용자&amp;nbsp;경험&amp;nbsp;우선&amp;nbsp;사고&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기능이 작동하는 것과 사용하기 편한 것은 다름&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시각적&amp;nbsp;피드백의&amp;nbsp;중요성&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 컴포넌트&amp;nbsp;분리의&amp;nbsp;중요성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하나의 거대한 컴포넌트보다 작은 단위로 분리하는 것이 유지보수에 유리&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;각&amp;nbsp;컴포넌트가&amp;nbsp;단일&amp;nbsp;책임을&amp;nbsp;가지도록&amp;nbsp;설계&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>LostBoard 프로젝트</category>
      <category>lostboard 프로젝트</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/70</guid>
      <comments>https://qweasd5123.tistory.com/70#entry70comment</comments>
      <pubDate>Sun, 21 Sep 2025 01:10:20 +0900</pubDate>
    </item>
    <item>
      <title>[LostBoard 프로젝트] 초기화면 디자인 수정</title>
      <link>https://qweasd5123.tistory.com/69</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;기존 초기화면&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qgWEz/btsMhtAIcKE/8rylspFOi38qvxBzAVuqlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qgWEz/btsMhtAIcKE/8rylspFOi38qvxBzAVuqlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qgWEz/btsMhtAIcKE/8rylspFOi38qvxBzAVuqlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqgWEz%2FbtsMhtAIcKE%2F8rylspFOi38qvxBzAVuqlk%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;1920&quot; height=&quot;875&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;초기화면 변경 이유&lt;/span&gt;&lt;/b&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;LostBoard를&amp;nbsp;처음&amp;nbsp;만들고&amp;nbsp;나서&amp;nbsp;몇&amp;nbsp;명의&amp;nbsp;친구들에게&amp;nbsp;보여줬는데,&amp;nbsp;공통적으로&amp;nbsp;나온&amp;nbsp;피드백이&amp;nbsp;있었습니다.&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'; background-color: #666666; color: #ffffff;&quot;&gt;&quot;이게&amp;nbsp;뭐하는&amp;nbsp;사이트야?&quot; &lt;br /&gt;&quot;Lost&amp;nbsp;Ark&amp;nbsp;관련&amp;nbsp;사이트&amp;nbsp;맞아?&quot; &lt;br /&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;맞는 말이었습니다. 기존 초기화면은 단순히 텍스트 필드와 버튼만 덩그러니 있어서, 처음 방문하는 사용자가 이 사이트의 목적을 파악하기 어려웠습니다.&lt;/p&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;b&gt; &amp;nbsp;기존&amp;nbsp;디자인의&amp;nbsp;문제점&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;b&gt;1. 정보 부족&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1758028630689&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존 코드 - 너무 심플함
&amp;lt;div className=&quot;content&quot;&amp;gt;
  &amp;lt;TextField
    label=&quot;캐릭터명&quot;
    variant=&quot;standard&quot;
    value={characterName}
    onChange={handleInputChange}
  /&amp;gt;
  &amp;lt;Button variant=&quot;contained&quot; onClick={handleSearch}&amp;gt;
    검색
  &amp;lt;/Button&amp;gt;
&amp;lt;/div&amp;gt;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;무엇을 검색하는 건지, 왜 검색해야 하는지 불분명&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Lost Ark와의 연관성을 알 수 없음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 시각적 정체성 부족&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1758028695963&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 기존 CSS - 밋밋한 디자인 */
.content {
  background-color: #1f2125;
  color: white;
  padding: 20px;
}&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Lost Ark의 느낌과 거리가 멀음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;브랜딩&amp;nbsp;요소&amp;nbsp;전혀&amp;nbsp;없음&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;개선&amp;nbsp;방향&amp;nbsp;설정&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선하면서&amp;nbsp;중점을&amp;nbsp;둔&amp;nbsp;4가지&amp;nbsp;포인트입니다:&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;명확한 정보 전달&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;b&gt;Lost Ark 테마 적용&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;b&gt;인터랙티브 요소&lt;/b&gt; - 반응성 있는 UI&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;반응형&amp;nbsp;디자인&amp;nbsp;&lt;/b&gt;- 모든 화면 사이즈에서 최적화&lt;/span&gt;&lt;/li&gt;
&lt;/ol&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt; &amp;nbsp;구체적인&amp;nbsp;개선&amp;nbsp;작업&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;b&gt;1. 브랜딩과 정보 전달 강화&lt;/b&gt;&lt;/span&gt;&lt;/h4&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;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758029036620&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div className=&quot;home-content&quot;&amp;gt;
  &amp;lt;h1 className=&quot;home-title&quot;&amp;gt;LostBoard&amp;lt;/h1&amp;gt;
  &amp;lt;p className=&quot;home-subtitle&quot;&amp;gt;
    Lost Ark 플레이어를 위한 레이드 효율성 분석 도구&amp;lt;br/&amp;gt;
  &amp;lt;/p&amp;gt;
  
  &amp;lt;div className=&quot;search-container&quot;&amp;gt;
    {/* 검색 UI */}
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선효과:&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;전문적이고&amp;nbsp;신뢰감&amp;nbsp;있는&amp;nbsp;첫인상&amp;nbsp;제공&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. Lost Ark 테마 색상&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;색상 팔레트 정의:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758029109722&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;:root {
  /* Lost Ark 시그니처 골드 */
  --primary-gold: #D4AF37;
  --primary-gold-light: #F4E4BC;
  --primary-gold-dark: #B8941F;
  --primary-gold-glow: rgba(212, 175, 55, 0.3);
  
  /* 다크 배경 */
  --bg-primary: #0A0A0B;
  --bg-secondary: #1A1B1E;
  --bg-tertiary: #2A2B2E;
  --bg-card: #1E1F22;
  
  /* 텍스트 계층 */
  --text-primary: #FFFFFF;
  --text-secondary: #B0B3B8;
  --text-muted: #8A8B8F;
}&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;제목 그라데이션 효과 적용:&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758029142301&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.home-title {
  font-size: 3.5rem;
  font-weight: 700;
  background: linear-gradient(135deg, var(--primary-gold), var(--primary-gold-light));
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  text-shadow: 0 0 30px var(--primary-gold-glow);
  letter-spacing: 2px;
}&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;개선 효과:&lt;/b&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;Lost Ark 게임 내 UI와 유사한 고급스러운 느낌&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;금색 포인트로 프리미엄 서비스 인상&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일관된&amp;nbsp;색상&amp;nbsp;시스템으로&amp;nbsp;브랜드&amp;nbsp;정체성&amp;nbsp;강화&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 인터랙티브한 검색 컨테이너&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;카드&amp;nbsp;형태의&amp;nbsp;검색&amp;nbsp;영역&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758029201124&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.search-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  padding: 40px;
  background: var(--bg-card);
  border-radius: 20px;
  border: 2px solid var(--border-color);
  box-shadow: var(--shadow-heavy);
  backdrop-filter: blur(10px);
  transition: all 0.3s ease;
  min-width: 400px;
}

.search-container:hover {
  border-color: var(--primary-gold);
  box-shadow: var(--shadow-gold);
  transform: translateY(-5px);
}&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Material-UI&amp;nbsp;입력&amp;nbsp;필드&amp;nbsp;커스터마이징:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758029225547&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;TextField
  id=&quot;character-search&quot;
  label=&quot;캐릭터명&quot;
  variant=&quot;outlined&quot;
  fullWidth
  sx={{
    '&amp;amp; .MuiOutlinedInput-root': {
      backgroundColor: 'var(--bg-tertiary)',
      color: 'var(--text-primary)',
      borderRadius: '12px',
      '&amp;amp; fieldset': {
        borderColor: 'var(--border-color)',
        borderWidth: '2px',
      },
      '&amp;amp;:hover fieldset': {
        borderColor: 'var(--primary-gold)',
      },
      '&amp;amp;.Mui-focused fieldset': {
        borderColor: 'var(--primary-gold)',
        boxShadow: '0 0 10px var(--primary-gold-glow)',
      },
    },
  }}
/&amp;gt;&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선 효과:&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;부드러운&amp;nbsp;애니메이션으로&amp;nbsp;모던한&amp;nbsp;느낌&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. 반응형 디자인 적용&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;반응형 디자인 최적화:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758029313317&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@media (max-width: 768px) {
  .home-title {
    font-size: 2.5rem;
    margin-bottom: 0.8rem;
  }
  
  .search-container {
    min-width: auto;
    width: 100%;
    max-width: 350px;
    padding: 30px 20px;
  }
  
  .home-subtitle {
    font-size: 1rem;
    padding: 0 20px;
  }
}

@media (max-width: 480px) {
  .home-title {
    font-size: 2rem;
  }
  
  .search-container {
    padding: 25px 15px;
  }
}&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개선 효과:&lt;/span&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;다양한&amp;nbsp;화면&amp;nbsp;크기에서&amp;nbsp;일관된&amp;nbsp;사용자&amp;nbsp;경험&lt;/li&gt;
&lt;/ul&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  개선 결과 비교&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;b&gt;기존 초기화면&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qgWEz/btsMhtAIcKE/8rylspFOi38qvxBzAVuqlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qgWEz/btsMhtAIcKE/8rylspFOi38qvxBzAVuqlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qgWEz/btsMhtAIcKE/8rylspFOi38qvxBzAVuqlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqgWEz%2FbtsMhtAIcKE%2F8rylspFOi38qvxBzAVuqlk%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;1920&quot; height=&quot;875&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;수정된 초기화면&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RjKVw/btsQA1H4zkq/LeGAxcIhUEr0aVy77EfkQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RjKVw/btsQA1H4zkq/LeGAxcIhUEr0aVy77EfkQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RjKVw/btsQA1H4zkq/LeGAxcIhUEr0aVy77EfkQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRjKVw%2FbtsQA1H4zkq%2FLeGAxcIhUEr0aVy77EfkQ0%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;1902&quot; height=&quot;954&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;954&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;사용자&amp;nbsp;반응&amp;nbsp;변화&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅&amp;nbsp;&quot;아,&amp;nbsp;Lost&amp;nbsp;Ark&amp;nbsp;사이트구나!&quot;&amp;nbsp;-&amp;nbsp;즉시&amp;nbsp;인식 &lt;br /&gt;✅&amp;nbsp;&quot;뭘&amp;nbsp;하는&amp;nbsp;사이트인지&amp;nbsp;바로&amp;nbsp;알겠어&quot;&amp;nbsp;-&amp;nbsp;명확한&amp;nbsp;정보&amp;nbsp;전달 &lt;br /&gt;✅&amp;nbsp;&quot;디자인이&amp;nbsp;게임이랑&amp;nbsp;잘&amp;nbsp;어울려&quot;&amp;nbsp;-&amp;nbsp;테마&amp;nbsp;일치성 &lt;br /&gt;✅ &quot;화면 크기를 변경해도 잘 보여&quot; - 반응형 디자인 효과&lt;/span&gt;&lt;/b&gt;&lt;/p&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;다음&amp;nbsp;개선&amp;nbsp;계획&lt;/span&gt;&lt;/b&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;초기화면&amp;nbsp;개선을&amp;nbsp;통해&amp;nbsp;얻은&amp;nbsp;인사이트:&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;일관된 디자인 시스템&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;b&gt;로딩 상태 개선&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;b&gt;애니메이션 강화&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;b&gt;접근성&amp;nbsp;개선&lt;/b&gt;&amp;nbsp;-&amp;nbsp;키보드&amp;nbsp;네비게이션&amp;nbsp;및&amp;nbsp;스크린&amp;nbsp;리더&amp;nbsp;지원&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;배운&amp;nbsp;점&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이번 디자인 개선을 통해 깨달은 것들&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 사용자 관점의 중요성&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 브랜딩의 힘&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단순히 기능만 잘 작동하는 것보다 정체성이 명확한 것이 중요&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;타겟&amp;nbsp;유저에게&amp;nbsp;친숙한&amp;nbsp;디자인&amp;nbsp;언어&amp;nbsp;사용의&amp;nbsp;효과&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 점진적 개선의 가치&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;한 번에 모든 걸 완벽하게 만들 필요 없음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자&amp;nbsp;피드백을&amp;nbsp;받고&amp;nbsp;단계적으로&amp;nbsp;개선하는&amp;nbsp;것이&amp;nbsp;더&amp;nbsp;효과적&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>LostBoard 프로젝트</category>
      <category>lostboard 프로젝트</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/69</guid>
      <comments>https://qweasd5123.tistory.com/69#entry69comment</comments>
      <pubDate>Tue, 16 Sep 2025 22:38:17 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js, MongoDB] 7. 상세페이지 만들기</title>
      <link>https://qweasd5123.tistory.com/68</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;URL 파라미터 문법&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748765682301&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get('/detail/:aaaa', (res, req) =&amp;gt; {
  req.send('detail.ejs')
})&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;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: start;&quot;&gt;URL 입력란에 '/detail/아무문자'로 접속하면 위 코드 실행&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: start;&quot;&gt;'아무문자' 입력란에 글의 id를 입력하면 DB에서 해당 id 글을 가져와서 출력하면 된다.&lt;/span&gt;&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: start;&quot;&gt;DB에서 특정 document 1개 찾기&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: start;&quot;&gt;findOne() 사용&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748765857718&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// a : 1 인 document를 찾는다
await db.collection().findOne({a : 1})&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;'_id'가 아래와 같은 document를 찾으려면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd5fGp/btsOk39r1WT/UethQmQHcFZlTekKOwNcvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd5fGp/btsOk39r1WT/UethQmQHcFZlTekKOwNcvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd5fGp/btsOk39r1WT/UethQmQHcFZlTekKOwNcvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd5fGp%2FbtsOk39r1WT%2FUethQmQHcFZlTekKOwNcvk%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;702&quot; height=&quot;297&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;297&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;pre id=&quot;code_1748765970792&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// _id 번호만 입력하는 것이 아닌 ObjectId까지 같이 입력해야 한다.
await db.collection('post').findOne({_id : new ObjectId('64bfde3b02d2932a4c06ffba')})&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;유저가 입력한 URL 가져오기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;유저가 입력한 파라미터 가져오기&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748766029245&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get('/detail/:aaaa', (res, req) =&amp;gt; {
  console.log(res.params)
})&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;입력한 파라미터에 해당하는 글 가져오기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748800181278&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get('/detail/:id', async (res, req) =&amp;gt; {
  let result = await db.collection('post').findOne({ _id : new ObjectId(res.params.id) })
  req.render('detail.ejs', { post : result })
})&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;예외처리&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748800312539&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get('/detail/:id', async (res, req) =&amp;gt; {
    try {
        let result = await db.collection('post').findOne({ _id: new ObjectId(res.params.id) })
        console.log(res.params)
        if (result == null) {
            req.status(404).send('잘못된 url')
        } else {
            req.render('detail.ejs', { post: result })
        }

    } catch (e) {
        console.log(e)
        req.status(404).send('잘못된 url')
    }
})&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;/list 페이지에서 제목을 클릭하면 해당 글의 상세페이지로 접속하기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;list.ejs&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748800258656&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
    &amp;lt;link href=&quot;/main.css&quot; rel=&quot;stylesheet&quot; /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body class=&quot;grey-bg&quot;&amp;gt;

    &amp;lt;%- include('nav.ejs') %&amp;gt;

    &amp;lt;div class=&quot;white-bg&quot;&amp;gt;
      &amp;lt;% for(let i = 0; i &amp;lt; posts.length; i++){ %&amp;gt;
      &amp;lt;div class=&quot;list-box&quot;&amp;gt;
        &amp;lt;a href=&quot;/detail/&amp;lt;%=posts[i]._id%&amp;gt;&quot; &amp;gt;&amp;lt;%= posts[i].title %&amp;gt;&amp;lt;/a&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;%= posts[i].content %&amp;gt;&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;% } %&amp;gt;
    &amp;lt;/div&amp;gt;
    
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Node.js, MongoDB</category>
      <category>MongoDB</category>
      <category>node.js</category>
      <category>코딩애플</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/68</guid>
      <comments>https://qweasd5123.tistory.com/68#entry68comment</comments>
      <pubDate>Sun, 1 Jun 2025 17:21:27 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js, MongoDB] 6. POST 요청</title>
      <link>https://qweasd5123.tistory.com/67</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;글 작성 페이지 생성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;write.ejs 레이아웃&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748625604367&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form class=&quot;form-box&quot;&amp;gt;
    &amp;lt;h4&amp;gt;글쓰기&amp;lt;/h4&amp;gt;
    &amp;lt;input&amp;gt;
    &amp;lt;input&amp;gt;
    &amp;lt;button type=&quot;submit&quot;&amp;gt;전송&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;input에 입력한 내용을 서버로 전송하기&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748625660276&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form class=&quot;form-box&quot; action=&quot;/URL&quot; method=&quot;POST&quot;&amp;gt;
   &amp;lt;h4&amp;gt;글쓰기&amp;lt;/h4&amp;gt;
    &amp;lt;input name=&quot;title&quot;&amp;gt;
    &amp;lt;input name=&quot;content&quot;&amp;gt;
    &amp;lt;button type=&quot;submit&quot;&amp;gt;전송&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input 태그에 name 속성을 넣어야 입력한 내용을 서버로 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL은 /add로 작명&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;서버에서 글 확인하기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748625826456&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.use(express.json())
app.use(express.urlencoded({extended:true}))&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&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 Light';&quot;&gt;&lt;b&gt;-app.use(express.json()):&lt;/b&gt; 클라이언트에서 보낸 요청(Request) 본문이 JSON 형식일 때, 그것을 &lt;b&gt;자동으로 JavaScript 객체로 파싱&lt;/b&gt;해서 req.body에 담아줍니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;-&lt;b&gt;app.use(express.urlencoded({extended:true})): &lt;/b&gt;HTML &amp;lt;form&amp;gt; 같은 방식으로 데이터를 보낼 때, 그 &lt;b&gt;데이터를 파싱해서&lt;/b&gt; req.body에 담아줍니다.&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;추출한 데이터를 DB에 저장하기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;write.ejs에서 /add로 데이터를 전송하기 때문에 POST 요청시 실행할 코드는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748626449778&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.post('/add', (res, req)=&amp;gt;{
  console.log(res.body)
  // 실행할코드~
})&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;res.body 데이터를 MongoDB로 전송시키기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748626850369&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let db
const url = 'MongoDB URL 주소'
new MongoClient(url).connect().then((client)=&amp;gt;{
  console.log('DB연결성공')
  db = client.db('forum')
  app.listen(8080, () =&amp;gt; {
    console.log('http://localhost:8080 에서 서버 실행중')
})
}).catch((err)=&amp;gt;{
  console.log(err)
})

// ~~~~~~~~~~

app.post('/add', async (res, req) =&amp;gt; {
    console.log(res.body)
    // 'forum'에 연결해 놓은 db 변수를 활용
    // insertOne() 함수를 사용하여 DB에 자료 추가
    await db.collection('post').insertOne({title : res.body.title, content : res.body.content})
  	// 자료 추가에 성공하면 '/list' 페이지로 연결
    req.redirect('/list')
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMi9Yk/btsOk56LUV5/NctMMs8esg1SOo0KFrqRSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMi9Yk/btsOk56LUV5/NctMMs8esg1SOo0KFrqRSK/img.png&quot; data-alt=&quot;write.ejs 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMi9Yk/btsOk56LUV5/NctMMs8esg1SOo0KFrqRSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMi9Yk%2FbtsOk56LUV5%2FNctMMs8esg1SOo0KFrqRSK%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;972&quot; height=&quot;650&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;write.ejs 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOIkxm/btsOk9BpeeN/NMv5zXLMDeoZ1OltTuHhX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOIkxm/btsOk9BpeeN/NMv5zXLMDeoZ1OltTuHhX1/img.png&quot; data-alt=&quot;제목과 내용을 입력하고 '전송' 버튼을 누르면 DB에 정상적으로 저장되는 것을 확인할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOIkxm/btsOk9BpeeN/NMv5zXLMDeoZ1OltTuHhX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOIkxm%2FbtsOk9BpeeN%2FNMv5zXLMDeoZ1OltTuHhX1%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;1420&quot; height=&quot;658&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;658&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;제목과 내용을 입력하고 '전송' 버튼을 누르면 DB에 정상적으로 저장되는 것을 확인할 수 있다.&lt;/figcaption&gt;
&lt;/figure&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;예외처리&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748762832490&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.post('/add', async (res, req) =&amp;gt; {
    console.log(res.body)
    try {
    	//제목이 빈칸이라면 경고문 출력
        if (res.body.title == '') {
            req.send('제목을 입력해주세요.')
        } else {
            await db.collection('post').insertOne({ title: res.body.title, content: res.body.content })
            req.redirect('/list')
        }
    } catch (e) {
    	// 그 밖의 에러 발생 예외처리
        console.log(e)
        req.status(500).send('서버에러')
    }
})&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Node.js, MongoDB</category>
      <category>MongoDB</category>
      <category>node.js</category>
      <category>코딩애플</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/67</guid>
      <comments>https://qweasd5123.tistory.com/67#entry67comment</comments>
      <pubDate>Sat, 31 May 2025 02:44:06 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js, MongoDB] 5. REST API</title>
      <link>https://qweasd5123.tistory.com/66</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;REST API 6가지 원칙&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;1. Uniform Interface&amp;nbsp;&lt;br /&gt;- 여러 URL과 method는 일관성이 있어야합니다.&lt;br /&gt;- 하나의 URL로는 하나의 데이터를 가져오게 디자인하는게 좋고, 간결하고 예측가능하게 URL과 method를 만드는게 좋다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;br /&gt;2. Client-server 역할 구분&amp;nbsp;&lt;br /&gt;-유저에게 서버역할을 맡기거나 DB를 직접 입출력하게 하면 좋지 않다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;3. Stateless&lt;br /&gt;-요청들은 서로 의존성이 있으면 안되고 각각 독립적으로 처리되어야합니다.&lt;br /&gt;&lt;br /&gt;4. Cacheable&lt;br /&gt;-서버가 보내는 자료들은 캐싱이 가능해야합니다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;5. Layered System&amp;nbsp;&lt;br /&gt;서버기능을 만들 때 레이어를 걸쳐서 코드가 실행되도록 만들어도 된다.&lt;br /&gt;&lt;br /&gt;6. Code on demand&lt;br /&gt;서버는&amp;nbsp;실행가능한&amp;nbsp;코드를&amp;nbsp;보낼&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;좋은 URL 작명법&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 단어들을 동사보다는 명사 위주로 구성함&lt;br /&gt;- 띄어쓰기는 언더바(_)대신 대시(-)사용&lt;br /&gt;- 파일 확장자 쓰지 말기 (.html 이런것)&lt;br /&gt;-&amp;nbsp;하위&amp;nbsp;문서들을&amp;nbsp;뜻할&amp;nbsp;땐&amp;nbsp;/&amp;nbsp;기호를&amp;nbsp;사용함&amp;nbsp;(하위폴더같은&amp;nbsp;느낌)&lt;/span&gt;&lt;/p&gt;</description>
      <category>Node.js, MongoDB</category>
      <category>MongoDB</category>
      <category>node.js</category>
      <category>코딩애플</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/66</guid>
      <comments>https://qweasd5123.tistory.com/66#entry66comment</comments>
      <pubDate>Wed, 28 May 2025 23:34:37 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js, MongoDB] 4. ejs</title>
      <link>https://qweasd5123.tistory.com/65</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;ejs 세팅&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;1. 터미널에 npm install ejs 입력해서 ejs 설치&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;2. 서버 파일 상단에 view engine 사용 코드 입력&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748423927254&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.set('view engine', 'ejs')&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;ejs 파일 만들어 보내주기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;*ejs 파일은 views라는 폴더 안에 생성하는 것이 일반적&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;&lt;span&gt;list.ejs 파일 생성후 간단한 html 코드 작성&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748424057040&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
    &amp;lt;link href=&quot;/main.css&quot; rel=&quot;stylesheet&quot; /&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body class=&quot;grey-bg&quot;&amp;gt;

  &amp;lt;div class=&quot;white-bg&quot;&amp;gt;
    &amp;lt;div class=&quot;list-box&quot;&amp;gt;
      &amp;lt;h4&amp;gt;글제목1&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;글내용1&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;list-box&quot;&amp;gt;
      &amp;lt;h4&amp;gt;글제목1&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;글내용2&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt; 

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1748424070986&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.grey-bg {
  background: #eee;
}
.white-bg {
  background: white;
  margin: 20px;
  border-radius: 5px;
}
.list-box {
  padding : 10px;
  border-bottom: 1px solid#eee;
}
.list-box h4{
  font-size: 16px;
  margin: 5px;
}
.list-box p {
  font-size: 13px;
  margin: 5px;
  color: grey;
}&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;ejs 파일에 데이터 넣는법&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748424117134&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get('/list', async (요청, 응답) =&amp;gt; {
  let result = await db.collection('post').find().toArray()
  응답.render('list.ejs', { posts : result })
})&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;위에서 받은 데이터를 .ejs 파일에서 출력하는 문법&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748424154534&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%= posts %&amp;gt;&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;위 list.ejs 파일 수정&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748424224120&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
    &amp;lt;link href=&quot;/main.css&quot; rel=&quot;stylesheet&quot; /&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body class=&quot;grey-bg&quot;&amp;gt;

  &amp;lt;div class=&quot;white-bg&quot;&amp;gt;
    &amp;lt;div class=&quot;list-box&quot;&amp;gt;
      &amp;lt;h4&amp;gt;&amp;lt;%= posts[0].title %&amp;gt;&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;&amp;lt;%= posts[0].content %&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;list-box&quot;&amp;gt;
      &amp;lt;h4&amp;gt;&amp;lt;%= posts[1].title %&amp;gt;&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;&amp;lt;%= posts[1].content %&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt; 

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&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;결과&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O2AZe/btsOfO5oheY/aNYyuJDmjdAuk3MkdikzQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O2AZe/btsOfO5oheY/aNYyuJDmjdAuk3MkdikzQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O2AZe/btsOfO5oheY/aNYyuJDmjdAuk3MkdikzQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO2AZe%2FbtsOfO5oheY%2FaNYyuJDmjdAuk3MkdikzQ1%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;758&quot; height=&quot;686&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;686&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;ejs 파일 안에서 자바스크립트 문법 활용하기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748426007757&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;white-bg&quot;&amp;gt;
      &amp;lt;% for(let i = 0; i &amp;lt; posts.length; i++){ %&amp;gt;
      &amp;lt;div class=&quot;list-box&quot;&amp;gt;
        &amp;lt;h4&amp;gt;&amp;lt;%= posts[i].title %&amp;gt;&amp;lt;/h4&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;%= posts[i].content %&amp;gt;&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;% } %&amp;gt;
&amp;lt;/div&amp;gt;&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;ejs 파일 안에 다른 ejs 파일 첨부하기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748426053707&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(views/nav.ejs)

&amp;lt;div class=&quot;nav&quot;&amp;gt;
  &amp;lt;a class=&quot;logo&quot;&amp;gt;Appleforum&amp;lt;/a&amp;gt;
  &amp;lt;a&amp;gt;page1&amp;lt;/a&amp;gt;
  &amp;lt;a&amp;gt;page2&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;


(list.ejs)

&amp;lt;%- include('nav.ejs') %&amp;gt;&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;lt;%- %&amp;gt; 와 &amp;lt;%= %&amp;gt; 차이&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;-&amp;lt;%- %&amp;gt; 의 경우 안의 내용이 html이라면 실제 html 처럼 렌더링 해준다. (ex. &lt;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: start;&quot;&gt;&amp;lt;%- &amp;lt;button&amp;gt; %&amp;gt;&amp;nbsp; -&amp;gt; 버튼 출력)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;-&amp;lt;%= %&amp;gt; 의 경우 안의 내용이 html이라도 html로 렌더링 하지 않고 실제 문자로 출력한다. (ex. &amp;lt;%= &amp;lt;button&amp;gt; %&amp;gt;&amp;nbsp; &amp;nbsp;-&amp;gt; &amp;lt;button&amp;gt; 출력)&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Node.js, MongoDB</category>
      <category>MongoDB</category>
      <category>node.js</category>
      <category>코딩애플</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/65</guid>
      <comments>https://qweasd5123.tistory.com/65#entry65comment</comments>
      <pubDate>Wed, 28 May 2025 18:39:39 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js, MongoDB] 3. MongoDB에서 데이터 받아오기</title>
      <link>https://qweasd5123.tistory.com/64</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;MongoDB 사이트에서 데이터 생성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yhKUm/btsOfmVHKRz/YbkKrkbv1eDdRSKw2kLIpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yhKUm/btsOfmVHKRz/YbkKrkbv1eDdRSKw2kLIpk/img.png&quot; data-alt=&quot;insert document 버튼을 눌러 2개의 데이터 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yhKUm/btsOfmVHKRz/YbkKrkbv1eDdRSKw2kLIpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyhKUm%2FbtsOfmVHKRz%2FYbkKrkbv1eDdRSKw2kLIpk%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;1390&quot; height=&quot;590&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;insert document 버튼을 눌러 2개의 데이터 생성&lt;/figcaption&gt;
&lt;/figure&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;MongoDB에 있는 데이터 가져오기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748419638449&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// /list 페이지에 접속하면 'post'에 있는 데이터 가져와서 출력하기
app.get('/list', async (요청, 응답) =&amp;gt; {
  let result = await db.collection('post').find().toArray()
  console.log(result)
  응답.send(result[0].title)
})&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;결과&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BU61e/btsOfrvTdAS/37UnktPVdETGk4diHyH1jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BU61e/btsOfrvTdAS/37UnktPVdETGk4diHyH1jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BU61e/btsOfrvTdAS/37UnktPVdETGk4diHyH1jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBU61e%2FbtsOfrvTdAS%2F37UnktPVdETGk4diHyH1jk%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;456&quot; height=&quot;259&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;259&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;데이터 나눠서 출력하기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748419766248&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 첫번째 게시물만 출력하기
app.get('/list', async (요청, 응답) =&amp;gt; {
    let result = await db.collection('post').find().toArray()
    console.log(result[0])
    응답.send('DB에 있던 게시물')
})

// 첫번째 게시물의 제목만 출력하기
app.get('/list', async (요청, 응답) =&amp;gt; {
    let result = await db.collection('post').find().toArray()
    console.log(result[0].title)
    응답.send('DB에 있던 게시물')
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Node.js, MongoDB</category>
      <category>MongoDB</category>
      <category>node.js</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/64</guid>
      <comments>https://qweasd5123.tistory.com/64#entry64comment</comments>
      <pubDate>Wed, 28 May 2025 17:10:15 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js, MongoDB] 2. MongoDB 세팅</title>
      <link>https://qweasd5123.tistory.com/63</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;MongoDB 데이터 저장 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- MongoDB는 document 데이터베이스라고 부르기도 하는데,&amp;nbsp; collection을 만들고 그안에 document를 만들어서 데이터를 기록해놓기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 이 document가 자바스크립트 object 자료와 똑같은 모습으로 기록하기 때문에 자바스크립트 코드 짜던것 그대로 데이터베이스에 넣을 수 있어서 저장과 출력이 편하다.&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;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;MongoDB 호스팅받기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MongoDB 가입 후 무료 호스팅 받기.&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;서버와 MongoDB 연결&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;1. 터미널을 열어서 npm install mongodb@5 입력(mongodb 라이브러리 설치)&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;2.&amp;nbsp; 호스팅 받은 mongodb 접속&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748268604558&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const { MongoClient } = require('mongodb')

let db
const url = 'mongodb사이트에 있던 님들의 접속 URL'
new MongoClient(url).connect().then((client)=&amp;gt;{
  console.log('DB연결성공')
  db = client.db('forum')
  // DB 접속 완료 후 서버를 띄우기
  app.listen(8080, () =&amp;gt; {
    console.log('http://localhost:8080 에서 서버 실행중')
  })

}).catch((err)=&amp;gt;{
  console.log(err)
})&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;mongoDB에 연결되었는지 확인&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748268685828&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// /news 페이지에 접속하면 DB에 데이터 저장하기
app.get('/news', ()=&amp;gt;{
  db.collection('post').insertOne({title : '뉴스'})
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1099&quot; data-origin-height=&quot;755&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN0PAr/btsOeHKzmMh/zIJR3Z0aeCvsKbGAdyworK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN0PAr/btsOeHKzmMh/zIJR3Z0aeCvsKbGAdyworK/img.png&quot; data-alt=&quot;title: '뉴스' 데이터가 저장된 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN0PAr/btsOeHKzmMh/zIJR3Z0aeCvsKbGAdyworK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN0PAr%2FbtsOeHKzmMh%2FzIJR3Z0aeCvsKbGAdyworK%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;1099&quot; height=&quot;755&quot; data-origin-width=&quot;1099&quot; data-origin-height=&quot;755&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;title: '뉴스' 데이터가 저장된 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Node.js, MongoDB</category>
      <category>MongoDB</category>
      <category>node.js</category>
      <author>qweasd5123</author>
      <guid isPermaLink="true">https://qweasd5123.tistory.com/63</guid>
      <comments>https://qweasd5123.tistory.com/63#entry63comment</comments>
      <pubDate>Mon, 26 May 2025 23:12:41 +0900</pubDate>
    </item>
  </channel>
</rss>