favicon

Jayden { do: smite }

231120(월)

🌱 성장일지 8.0

행복한 이기주의자(웨인 다이어)의 내용에 자극받아 시작하는 소박한 성장기록

  • 살아있는 꽃과 죽은 꽃은 어떻게 구별하는가?
  • 성장하고 있는 것이 살아 있는 것이다.
  • 생명의 유일한 증거는 성장이다!

⚛ (8.0)<완전 개편> 그 날의 키워드 중심으로 간단하게 정리하되 매일 꾸준히 작성할 수 있는 공간을 만들어보자.

개인 블로그 react-markdown 이미지 src 에러 해결

현재 이 블로그의 posts 페이지에서 각 post들의 상세 페이지Markdown이라는 컴포넌트를 만들고 react-markdown을 적용하여 사용하고 있다.

헌데, 작성한 블로그 마크다운 파일에서 ![이미지](상대경로)로 작성하니 이미지가 렌더링되지 않았다. 먼저 Markdown 컴포넌트의 구조를 보면 아래와 같다.

'use client'; import ReactMarkdown from 'react-markdown'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { materialDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; import remarkGfm from 'remark-gfm'; import Image from 'next/image'; type Props = { children: string; }; export default function Markdown({ children }: Props) { const markdownClassName = { headings: 'prose-headings:text-slate-700 dark:prose-headings:text-slate-100', code: 'prose-code:text-inflearn-0', blockquote: 'prose-blockquote:text-jayden-0', a: 'prose-a:text-black-0 dark:prose-a:text-white prose-a:underline', pre: 'prose-pre:w-full prose-pre:overflow-x-auto prose-pre:border prose-pre:border-slate-700 dark:prose-pre:border-slate-100 prose-pre:rounded prose-pre:my-4 prose-pre:py-2 prose-pre:px-4 prose-pre:shadow', }; // TODO: prose-base, sm, lg, xl 등 사용하여 글 축소 및 확대 기능 고려해보기 return ( <ReactMarkdown className={`prose-base w-full text-slate-700 dark:text-slate-100 ${markdownClassName.headings} ${markdownClassName.code} ${markdownClassName.blockquote} ${markdownClassName.a} ${markdownClassName.pre}`} remarkPlugins={[remarkGfm]} components={{ // ... 생략 img: image => { return <Image src={image.src || ''} alt={image.alt || ''} width={500} height={500} />; }, }} > {children} </ReactMarkdown> ); }

보는 것처럼 next/image에서 제공하는 Image 컴포넌트를 사용하고 있다. 그런데 Image 컴포넌트는 src 속성에 상대경로를 사용할 수 없다. 그래서 public 폴더에 이미지를 넣고 src 속성에 절대경로를 사용해야한다. 그래서 Markdown 컴포넌트에서 Image 컴포넌트를 사용할 때 src 속성에 상대경로를 사용하면 src 속성에 절대경로를 사용하라는 에러가 발생한다.

그리고 markdown 파일에서 이미지를 사용할 때는 ![](public 내부의 경로)를 작성해야 한다. 예를 들어 public 폴더 안에 images 폴더 안에 1.png라는 파일이 있다면 ![](/images/1.png)로 작성해야 한다. 이 부분은 next/image에서 Image 컴포넌트를 사용할 때 src 속성에 절대경로를 사용해야하기 때문이다. 문제는 md 파일에서 제공하는 파일 경로 자동완성은 상대경로를 제공한다는 것이다. 이 부분을 글 작성할 때 조심해야한다.

Vue

Vue는 UI를 개발하기 위한 JS 프레임워크이다. 여기서 중요한 건 react가 라이브러리인 반면에 vue는 프레임워크라는 점이다. 그렇기 때문에 react는 개발자가 필요한 라이브러리를 선택해서 사용할 수 있지만 vue는 이미 정해진 라이브러리를 사용해야한다.

Options API vs Composition API

Options API

Vue 2에서 소개된 전통적인 방식으로, 컴포넌트 옵션 객체를 사용한다. 여기에는 data, methods, computed, watch, lifecycle hooks 등의 속성을 제공한다.

장점

  • 직관적이고 익숙하다. 즉, 새로운 사용자가 배우기 쉽고 이해하기 쉬운 구조이다.
  • 컴포넌트의 옵션들이 명확하게 구분되어 있어 코드를 쉽게 조직할 수 있다.

단점

  • 같은 기능에 대한 코드가 컴포넌트의 여러 부분에 흩어질 수 있어, 크고 복잡한 컴포넌트에서 관리하기 어렵다. 즉, 관심사 분리가 어렵다.
  • 코드 재사용을 위한 믹스인이나 확장이 복잡하고 충돌이 발생할 수 있다. 즉, 재사용성이 제한된다.

참고로 Vue 2는 2023년 12월 31일까지만 지원 예정이다.

<script> export default { // Properties returned from data() become reactive state // and will be exposed on `this`. data() { return { count: 0, }; }, // Methods are functions that mutate state and trigger updates. // They can be bound as event handlers in templates. methods: { increment() { this.count++; }, }, // Lifecycle hooks are called at different stages // of a component's lifecycle. // This function will be called when the component is mounted. mounted() { console.log(`The initial count is ${this.count}.`); }, }; </script> <template> <button @click="increment">Count is: {{ count }}</button> </template>

Composition API

Vue 3에서 도입된 새로운 방식으로, setup() 함수 내에서 구성 요소의 기능을 정의한다. 이 API를 사용하면 반응형 상태, 계산된 속성, 메서드 등을 직접 만들고 조합할 수 있다.

장점

  • 관련된 로직을 하나의 장소에 모아 관리할 수 있어, 큰 규모의 애플리케이션에서 유리하다. 즉, 관심사를 집중시킬 수 있다.
  • 커스텀 훅을 만들어 코드를 재사용하고 조직할 수 있어, 유지보수와 테스트가 용이하다. 즉, 재사용성과 조직성이 향상된다.

단점

  • 기존 Vue 사용자에게는 새로운 개념과 패턴을 익혀야 하므로 초기 학습 곡선이 더 가파르다.
  • 단순한 애플리케이션에서는 과도한 복잡성을 초래할 수 있다.
<script setup> import { ref, onMounted } from 'vue'; // reactive state const count = ref(0); // functions that mutate state and trigger updates function increment() { count.value++; } // lifecycle hooks onMounted(() => { console.log(`The initial count is ${count.value}.`); }); </script> <template> <button @click="increment">Count is: {{ count }}</button> </template>

📝 회고

블로그 post 상세 페이지의 img 태그 경로 에러를 해결하느라 시간을 많이 써버렸다. 알고보니 next의 Image 컴포넌트 절대 경로 문제였는데, 혼자서 react-markdown쪽 로직을 잘못 짠줄 알고 이것저것 건드리다가 더 꼬여버렸었다. Vue는 이제 조금 찍먹으로 보고 있는데, 왜 대부분의 개발자분들이 React보다 Vue가 좀더 직관적이라고 말하셨는지 알 것 같다. 지금은 간단한 예제 코드만 본 거라, 이렇다 저렇다 말하기 이르긴 하지만 뭔가 좀더 직관적이라는 느낌이 든다. 그리고 Vue 3에서 Composition API를 도입하면서 React의 Hook과 비슷한 느낌이 든다. 이 부분은 좀 더 공부해봐야겠다.

참고

Copyright 2023. all rights reserved by Jayden