<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hoan_c.log</title>
        <link>https://velog.io/</link>
        <description>Day One</description>
        <lastBuildDate>Fri, 03 Oct 2025 23:52:10 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hoan_c.log</title>
            <url>https://velog.velcdn.com/images/hoan_c/profile/cdfee00e-714a-4595-83b2-5b493e8c4967/image.PNG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hoan_c.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hoan_c" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[lightweight-charts 사용법]]></title>
            <link>https://velog.io/@hoan_c/lightweight-charts-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@hoan_c/lightweight-charts-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Fri, 03 Oct 2025 23:52:10 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hoan_c/post/0883f6f4-b038-4b2d-ac20-625b05e604fa/image.png" alt="">
☝️플젝 중 만들어낸 그래프. 좀 이쁜 듯?</p>
<h2 id="개요">개요</h2>
<blockquote>
<ul>
<li>TradingView 에서 만든 경량, 고성능 차트 라이브러리</li>
</ul>
</blockquote>
<ul>
<li>주식과 같은 그래프 구현시 용이하게 쓸 수 있다.
<a href="https://tradingview.github.io/lightweight-charts/docs">공식 문서 링크</a></li>
</ul>
<h2 id="설치">설치</h2>
<pre><code>npm install lightweight-charts</code></pre><h2 id="사용">사용</h2>
<p>아래 2가지 개념만 있으면 누구나 사용할 수 있을 정도로 간펀하면서 쉽다.</p>
<blockquote>
<ul>
<li>createChart -&gt; 차트 배경 생성</li>
</ul>
</blockquote>
<ul>
<li>.addLineSeries -&gt; 그래프 생성</li>
</ul>
<pre><code class="language-TypeScript">import { useEffect, useRef } from &#39;react&#39;;
import { createChart } from &#39;lightweight-charts&#39;;


const MyChart = () =&gt; {
  //차트배경을 가리키는 Ref
  const chartContainerRef = useRef&lt;HTMLDivElement&gt;(null);  


  useEffect(() =&gt; {
    if (!chartContainerRef.current) return; //요소 생선 전 차트 생성 방지

    // createChart(container, options) → 차트 배경 생성 (지정 DOM 요소)
    const chart = createChart(chartContainerRef.current, { 
      width: chartContainerRef.current.clientWidth,
      height: 300,
    });


    // 그래프 추가
    //Candles, Line, Bars, Area, BaseLine, Histogram
    //addLineSeries(그래프 형태, 옵션설정)
    const graph = chart.addSeries(Line);

    // 데이터 넣기
    graph.setData([
      { time: &#39;2025-01-01&#39;, value: 100 },
      { time: &#39;2025-01-02&#39;, value: 110 },
      { time: &#39;2025-01-03&#39;, value: 105 },
      { time: &#39;2025-01-04&#39;, value: 120 },
    ]);

    // 리사이즈 대응
    const handleResize = () =&gt; {
      chart.applyOptions({ width: chartContainerRef.current!.clientWidth });
    };
    window.addEventListener(&#39;resize&#39;, handleResize);

    // 컴포넌트 언마운트시 메모리 관리를 위한 remove
    return () =&gt; {
      window.removeEventListener(&#39;resize&#39;, handleResize);
      chart.remove();
    };
  }, []);

  return (
    &lt;div ref={chartContainerRef} style={{ width: &#39;100%&#39;, height: &#39;300px&#39; }} /&gt;
  );
};

export default MyChart;
</code></pre>
<h2 id="옵션">옵션</h2>
<blockquote>
<p>커스텀할 수 있는 부분이 많은 만큼, 옵션 또한 엄청 많다.</p>
</blockquote>
<h3 id="차트-배경-옵션createchart">차트 배경 옵션(createChart)</h3>
<ul>
<li>width, height(차트 크기)</li>
<li>layout (배경/글자 색상, 폰트)</li>
<li>grid (세로/가로 라인 색상 및 스타일)</li>
<li>crosshair (마우스 올렸을 때 보이는 십자선 모드)</li>
<li>rightPriceScale / leftPriceScale (가격축 보이기 여부, 색상)</li>
<li>timeScale (시간축 옵션)</li>
<li>handleScroll / handleScale (마우스/터치 스크롤, 줌 허용 여부)<pre><code>const chart = createChart(container, {
width: 800, //width, height(차트 크기)
height: 400,
layout: { //배경/글자 색상, 폰트
  background: { color: &#39;#ffffff&#39; },
  textColor: &#39;#333&#39;,
  fontSize: 12,
},
grid: { //세로/가로 라인 색상 및 스타일
  vertLines: { color: &#39;#e1e1e1&#39;, style: 0 },
  horzLines: { color: &#39;#e1e1e1&#39;, style: 0 },
},
crosshair: { //crosshair (마우스 올렸을 때 보이는 십자선 모드)
  mode: 1,
},
rightPriceScale: { // or leftPriceScale 가격축 보이기 여부, 색상
  visible: true,
  borderColor: &#39;#ccc&#39;,
},
timeScale: { //시간축 옵션
  borderColor: &#39;#ccc&#39;,
  timeVisible: true,
  secondsVisible: false,
},
handleScroll: { //마우스/터치 스크롤
  mouseWheel: true,
  pressedMouseMove: true,
},
handleScale: { //줌 허용 여부
  axisPressedMouseMove: true,
  pinch: true,
  mouseWheel: true,
},
});</code></pre></li>
</ul>
<hr>
<h3 id="그래프-옵션addseries">그래프 옵션(addSeries)</h3>
<h4 id="candlestick-series">Candlestick Series</h4>
<p><img src="https://velog.velcdn.com/images/hoan_c/post/a03fbf67-a397-4a3e-a425-f8ac652713e7/image.png" alt=""></p>
<pre><code>const candleSeries = chart.addSeries(CandlestickSeries, {
  upColor: &#39;#26a69a&#39;,
  downColor: &#39;#ef5350&#39;,
  borderUpColor: &#39;#26a69a&#39;,
  borderDownColor: &#39;#ef5350&#39;,
  wickUpColor: &#39;#26a69a&#39;,
  wickDownColor: &#39;#ef5350&#39;,
  priceLineVisible: true,
});</code></pre><ul>
<li>upColor / downColor: 양봉/음봉 몸통 내부 색</li>
<li>borderUpColor / borderDownColor: 몸통의 테두리 색</li>
<li>wickUpColor / wickDownColor: 위·아래 심지(꼬리)의 색</li>
<li>priceLineVisible: 현재가 라인(수평선)을 표시할지 여부</li>
</ul>
<hr>
<h4 id="line-series">Line Series</h4>
<p><img src="https://velog.velcdn.com/images/hoan_c/post/73f8d949-5d9e-47ff-9937-8375aa3aea64/image.png" alt=""></p>
<pre><code>const lineSeries = chart.addSeries(LineSeries, {
  color: &#39;#2962FF&#39;,
  lineWidth: 2,
  lineStyle: 0, // 0: solid, 1: dotted, 2: dashed
  priceLineVisible: true,
  crossHairMarkerVisible: true,
});</code></pre><ul>
<li>color: 선의 색상</li>
<li>lineWidth: 선의 두께</li>
<li>lineStyle: 선의 스타일 (실선, 점선 등)</li>
<li>crosshairMarkerVisible: 크로스헤어가 있을 때 지점 마커를 보일지 여부</li>
<li>priceLineVisible: 현재가 라인 보이기 여부</li>
</ul>
<hr>
<h4 id="histogram-series">Histogram Series</h4>
<p><img src="https://velog.velcdn.com/images/hoan_c/post/a72658d1-11eb-486c-95ae-6945f7a5bf3f/image.png" alt=""></p>
<pre><code>const volumeSeries = chart.addSeries(HistogramSeries, {
  color: &#39;#26a69a&#39;,
  base: 0,
  priceFormat: { type: &#39;volume&#39; },
});</code></pre><ul>
<li>color: 막대의 기본 색</li>
<li>base: 기준선 값 (보통 0)</li>
<li>priceFormat: 값의 표시 방식 (예: volume, percent 등)</li>
</ul>
<hr>
<h4 id="area-series">Area Series</h4>
<p><img src="https://velog.velcdn.com/images/hoan_c/post/004eb90f-67bc-42a4-82d4-4ad060a379af/image.png" alt=""></p>
<pre><code>const areaSeries = chart.addSeries(AreaSeries, {
  lineColor: &#39;#2962FF&#39;,
  topColor: &#39;rgba(41, 98, 255, 0.5)&#39;,
  bottomColor: &#39;rgba(41, 98, 255, 0.0)&#39;,
  lineWidth: 2,
});</code></pre><ul>
<li>lineColor: 영역 상단 경계선 색</li>
<li>topColor: 라인 위쪽 영역 색</li>
<li>bottomColor: 라인 아래쪽 영역 색</li>
<li>lineWidth: 경계선의 두께</li>
</ul>
<hr>
<h4 id="bar-series">Bar Series</h4>
<p><img src="https://velog.velcdn.com/images/hoan_c/post/9f7d4111-59bc-4e1f-ba08-d7c9795e3017/image.png" alt=""></p>
<pre><code>const bar = chart.addSeries(BarSeries, {
  upColor: &#39;#26a69a&#39;,
  downColor: &#39;#ef5350&#39;,
  thinBars: false,
});</code></pre><ul>
<li>upColor / downColor: 가격 상승/하락 시 막대 색</li>
<li>thinBars: 얇은 막대 스타일 여부</li>
</ul>
<hr>
<h4 id="baseline-serise">Baseline Serise</h4>
<p><img src="https://velog.velcdn.com/images/hoan_c/post/a02fe250-cb2d-42c7-8de2-40a0cb18b9e2/image.png" alt=""></p>
<pre><code>const baseline = chart.addSeries(BaselineSeries, {
  baseValue: 100,
  topFillColor1: &#39;rgba(0, 200, 0, 0.3)&#39;,
  topFillColor2: &#39;rgba(0, 200, 0, 0.0)&#39;,
  bottomFillColor1: &#39;rgba(200, 0, 0, 0.3)&#39;,
  bottomFillColor2: &#39;rgba(200, 0, 0, 0.0)&#39;,
  topLineColor: &#39;green&#39;,
  bottomLineColor: &#39;red&#39;,
  lineWidth: 2,
  lineStyle: 0,
  lineVisible: true,
  crosshairMarkerVisible: true,
});</code></pre><ul>
<li>baseValue: 기준선 값</li>
<li>topFillColor1 / topFillColor2: 기준선 위쪽 영역 그라디언트 색상</li>
<li>bottomFillColor1 / bottomFillColor2: 기준선 아래쪽 영역 그라디언트 색상</li>
<li>topLineColor / bottomLineColor: 기준선 위/아래 경계선 색상</li>
<li>lineWidth: 기준선 경계선 두께</li>
<li>lineStyle: 기준선 스타일 (solid, dashed 등)</li>
<li>lineVisible: 기준선 경계선을 보일지 여부</li>
<li>crosshairMarkerVisible: 크로스헤어가 지점 마커를 표시할지 여부</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 FSD 아키텍쳐, 기존 문제점, 적용법]]></title>
            <link>https://velog.io/@hoan_c/%EB%A6%AC%EC%95%A1%ED%8A%B8-FSD-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%EA%B8%B0%EC%A1%B4-%EB%AC%B8%EC%A0%9C%EC%A0%90-%EC%A0%81%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@hoan_c/%EB%A6%AC%EC%95%A1%ED%8A%B8-FSD-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%EA%B8%B0%EC%A1%B4-%EB%AC%B8%EC%A0%9C%EC%A0%90-%EC%A0%81%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Mon, 29 Sep 2025 17:26:29 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<blockquote>
<p>지금까지 폴더 구조에 대해 <strong>고민해본적 없었다</strong>.</p>
</blockquote>
<p>개인, 팀프로젝트는 화면상 기능, 뎁스가 깊지 않았고,
인턴 시절엔 이미 폴더 구조가 정해져 있었으니 그에 대해 적응하면 그만이었다.</p>
<p><img src="https://velog.velcdn.com/images/hoan_c/post/feb1192f-95a5-47a8-a40e-9334c7490995/image.png" alt=""></p>
<p>하지만 실제 서비스 할 제품을 만들면서 부터는 고민이 깊어지기 시작했다.
물론 MVP를 빠른 속도로 개발 하는 것이 1순위라 생각했지만,
아예 무시하고 개발하기엔 이후에 발생할 유지보수비용이 너무나 커질 것만 같았다.</p>
<p>그래서 찾아본게
<a href="https://feature-sliced.design/kr/docs/get-started/overview">Feature-Sliced Design</a>
FSD 아키텍쳐이다.</p>
<hr>
<h2 id="기존-문제점">기존 문제점</h2>
<blockquote>
<p>기존엔 레이어, 도메인 기반으로만 코드를 나누고 있었다.</p>
</blockquote>
<h3 id="상대경로-지옥">상대경로 지옥</h3>
<pre><code>import Logo from &#39;../../../../../../../assets/logo.png&#39;;</code></pre><ul>
<li>설명할 필요도 없다</li>
</ul>
<h3 id="재사용-단위-불명확">재사용 단위 불명확</h3>
<pre><code>src/components/common/
  └── TokenListCard.tsx  # ❌ 특정 페이지에서만 쓰는 컴포넌트</code></pre><ul>
<li>정작 공용으로 만들어두고 한 페이지에서만 사용</li>
</ul>
<h3 id="중복-경로-혼선">중복 경로 혼선</h3>
<pre><code>src/pages/exchange/...
src/pages/add/exchange/...</code></pre><ul>
<li>코드 작성자가 봐도 헷갈리는 경로</li>
</ul>
<p>이런 문제들을 해결할 수 있는 아키텍쳐라 본다.</p>
<hr>
<h2 id="fsd-아키텍쳐-간단-설명">FSD 아키텍쳐 간단 설명</h2>
<blockquote>
<p>기능 단위로 프로젝트를 분할, 각 계층에서 책임을 분리</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hoan_c/post/8d5c3f7f-76be-4f8b-b0ac-f63a9b005162/image.png" alt=""></p>
<h3 id="layers">Layers</h3>
<blockquote>
<p>모든 FSD 프로젝트의 표준 최상위 폴더</p>
</blockquote>
<pre><code>src/
├── app/          # 애플리케이션 로직이 초기화되는 곳입니다. 프로바이더, 라우터, 전역 스타일 등 정의. 애플리케이션의 진입점 역할.
├── processes/    # 온보딩, 회원가입과 같이 한 기능에 여러 페이지(더이상 안쓰임)
├── pages/        # 애플리케이션의 페이지
├── widgets/      # 페이지에 사용되는 독립적인 UI 컴포넌트
├── features/     # (선택적) 비즈니스 가치를 전달하는 사용자 시나리오와 기능(좋아요, 리뷰 작성, 제품 평가 등).
├── entities/     # (선택적) 사용자, 리뷰, 댓글 등
└── shared/       # 로직에 종속되지 않은 재사용 가능한 컴포넌트와 유틸리티(UI 키트, axios 설정, 애플리케이션 설정, 비즈니스 로직)
</code></pre><h3 id="slice">Slice</h3>
<blockquote>
<p>Layer 내부를 비즈니스 도메인별로 나눈 두 번째 수준.</p>
</blockquote>
<pre><code>src/
├── app/
│    ├── providers/
│    ├── styles/
│    └── index.tsx
├── pages/
│    ├── home/
│    ├── profile/
│    └── about/
├── widgets/
│    ├── newsfeed/
│    ├── catalog/
│    ├── header/
│    └── footer/
├── features/
│    ├── user/
│    ├── auth/
│    ├── favorites/
│    └── filter-users/
├── entities/
│    ├── user/
│    └── session/
└── shared/  </code></pre><ul>
<li>이름이나 개수에 제한이 없다. 위 사진에 나와있는 것들은 말 그대로 예시.</li>
<li>같은 Layer 내 다른 Slice를 참조할 수 없다. 이 규칙이 높은 응집도와 낮은 결합도를 보장한다.</li>
</ul>
<h3 id="segment">Segment</h3>
<blockquote>
<p>Slice와 App·Shared Layer는 Segment로 세분화</p>
</blockquote>
<ul>
<li>기술적 목적에 따라 코드를 그룹화. 일반적으로 다음과 같은 Segment를 사용.</li>
</ul>
<pre><code>ui - UI components, date formatter, styles 등 UI 표현과 직접 관련된 코드
api - request functions, data types, mappers 등 백엔드 통신 및 데이터 로직
model - schema, interfaces, store, business logic 등 애플리케이션 도메인 모델
lib - 해당 Slice에서 여러 모듈이 함께 사용하는 공통 library code
config - configuration files, feature flags 등 환경·기능 설정</code></pre><h2 id="점진적-도입-방법">점진적 도입 방법</h2>
<blockquote>
<p>리팩토링 중에는 새로운 대규모 Entity 추가를 피하는 것을 추천.</p>
</blockquote>
<h3 id="기존-프로젝트에-fsd를-도입하는-방법">기존 프로젝트에 FSD를 도입하는 방법</h3>
<h4 id="1-app-shared-layer를-먼저-정리하며-기반을-다짐">1. app, shared Layer를 먼저 정리하며 기반을 다짐.</h4>
<h4 id="2-기존-ui를-widgets-pages-layer로-대략-분배">2. 기존 UI를 widgets, pages Layer로 대략 분배.</h4>
<p>위 과정에서 FSD 규칙을 위반은 괜찮음.</p>
<h4 id="3-import-위반을-하나씩-해결하면서-entities-features를-추출">3. Import 위반을 하나씩 해결하면서, entities, features를 추출.</h4>
<hr>
<h2 id="마치며">마치며</h2>
<blockquote>
<p>정답은 없다.
다만, 오답은 있다.</p>
</blockquote>
<p>오죽 했으면 <a href="https://legacy.reactjs.org/docs/faq-structure.html?utm_source=chatgpt.com">리액트 공식 문서</a>에서 조차 폴더 아키텍쳐에 대해서 별 다른 의견이 없다고 하겠는가?</p>
<p>하지만 오답은 있다.
너무나 많은 중첩, 너무 높은 의존도, 이에 대한 너무 많은 생각과 고민은 피하라고 한다.</p>
<p>효율적이라 생각는 방법이 다른 사람들을 납득시킬 수 있으면
그게 정답이지 않을까?</p>
<hr>
<h2 id="📌-참고-문헌">📌 참고 문헌</h2>
<p><a href="https://feature-sliced.design/kr/docs/get-started/overview">FSD 공식 문서</a>
<a href="https://emewjin.github.io/feature-sliced-design/?utm_source=substack&amp;utm_medium=email">emewjin 님의 깃헙 블로그</a></p>
]]></description>
        </item>
    </channel>
</rss>