NEW

1

/ 5

[3] 타입스크립트 선택적 타입을 통해 누락된 값이 있어도 타입 추론을 해보기

​개요여러가지를 통해 api로 값을 받아올때면 일부 값은 누락되어 오는 경우가 허다하게 발생하는데,이번에는 타입스크립트의 선택적 타입 정의를 통해 특정 값이 누락되어도 타입 정의를 통해 추론이 잘 이루어질 수 있게끔 해보겠습니다!선택적 타입 정의?타입을 정의하게 되면 해당 타입과 관련된 값이 꼭 존재해야하는 불편함이 있는데선택적 타입 정의를 사용하게 되면 값의 존재 유무에 따라 타입을 검사해주는 고마운 친구였습니다 ㅎ​​사용방법​코드보기export type UserCardProps = { name: string; age?: number; // 선택적 props skills?: string[]; // 선택적 배열 props}선택적 타입 정의를 위해서는 원하시는 값에다가 ?를 뒤에 붙여주시면 됩니다.그럼 아래와 같은 상황에서 age가 없으면 타입 추론을 안하게 되며,age 존재시 age의 타입이 number인지 추론을 하게 됩니다.코드보기export const UserCard = ({ name, age, skills = [], address }: UserCardProps) => {}​​마치며알면 알수록 편리하고 다양한 기능이 타입스크립트에 있는거 같네요정말 스크립트의 세계는 방대하네요...

썸네일
썸네일
썸네일
썸네일

NEW

2

/ 5

클로저란 무엇일까

사진: Unsplash의 Mārtiņš Zemlickis개요사실상 면접을 대비한 공부라고 할수도 있고일상생활에 있다보니 그러려니 하고 쓰다가 명칭을 알게된 경우였는데이번 기회에 클로저에 대해 알기쉽게 이해해보고자 작성하게 되었습니다.클로저란?쉽게 얘기하면 클로저란 함수 자체에 독자적인 변수가 존재하고 이걸 콜백함수로 제어가 가능해야 하며 이 함수를 변수에 담아 호출해야 합니다,스코프(변수의 범위)가 함수와 그 함수가 선언될 당시의 스코프를 기억하는 구조로,이를 통해 함수가 외부 스코프의 변수에 접근할 수 있도록 해줍니다.조건은 뭘까?1.내부 함수가 존재해야 합니다.2.이 내부 함수가 외부 변수에 접근하고 있어야 합니다.3.이 내부 함수가 반환되어 외부에서 호출될 수 있어야 합니다.특성은 뭐가 있을까?변수 접근: 클로저를 통해 내부 함수는 외부 함수의 변수를 계속해서 참조할 수 있어서 이로 인해 외부 함수가 실행된 후에도 변수의 값을 사용할 수 있게 됩니다.정보 은닉: 클로저를 사용하면 특정 변수를 외부에서 직접 접근하지 못하도록 숨길 수 있어서 이렇게 하면 데이터의 캡슐화가 이루어지고, 해당 데이터에 대한 제어가 가능해집니다.상태 유지: 클로저를 통해 생성된 내부 함수는 외부 변수의 상태를 기억하고, 이 상태를 변화시킬 수 있어서 그래서 특정 기능을 구현할 때 유용하게 사용됩니다.예시클로저를 생성하는법const counter = makeCounter();클로저의 핵심은 함수를 호출했을때 그 결과가 콜백함수여야 한다는 것입니다.그래야 함수 속 외부 변수를 이용해 콜백함수로 그 외부변수에 값을 변화 시킬 수 있겠죠?​쉬운 예제const makeCounter = () => {    let like = 0; // 외부 변수    return () => { // 콜백 함수 = 클로저        return like += 1;    };};// 클로저 생성const counter = makeCounter();ㅊconsole.log(counter()); // like 변수의 값 반환 1console.log(counter()); // like 변수의 값 반환 2console.log로 makeCounter가 호출될때마다 like += 1 를 반환해주고 있기 때문에,해당 변수의 반환된 값이 counter 상수에 저장되어 사용되어집니다.비스무리한 다른 예제function makeCounter() { let count = 0; // 외부 변수 return function() { count += 1; // 외부 변수를 변경 return count; // 변경된 외부 변수를 반환 };}const counter = makeCounter(); // 클로저 생성console.log(counter()); // 1console.log(counter()); // 2console.log(counter()); // 3makeCounter 함수에는 독자적인 count라는 변수가 있고그 외부 변수를 제어 하고 반환하는 콜백함수를 리턴하고 있으며makeCounter 함수를 counter라는 상수에 담아console.log로 호출해 사용하고 있습니다.클로저가 아닌 경우다음과 같은 경우는 클로저가 아닙니다.let count = 0;const increment = () => { count += 1; // 외부 변수에 접근하지만, 반환되는 것이 없음};increment(); // 단순히 호출만 됨단순히 외부 변수를 접근해 변화시키고만 있지increment함수 자체가 변수를 기억하고 반환되는것이 아니기 때문에 클로저라 할 수 없습니다.

썸네일
썸네일
썸네일
썸네일

NEW

3

/ 5

[2] 타입스크립트의 타입 정의 상속에 대하여

사진: Unsplash의 Markus Spiske​개요저번에는 타입을 어떻게 정의하고 사용하는지에 대해 알아봤는데이번에는 상속을 이용해서 타입을 조합해 정의해보겠습니다!타입 정의 방법type Test = { name: string, // 이름 description: string, // 설명 age: number, // 나이 func: () => void // 함수}테스트로 타입을 정의 해보았습니다.이 부분이 저번에 알려드린 내용과 같은데요.하지만 이렇게 사용하게 되면 특수한 상황,예를 들면 name과 age의 타입만 필요한 상황, func와 name의 타입 정의만 필요한 상황 같은 특수한 상황이 있을 수 있는데요이럴때 필요한게 상속입니다.상속이란?마치 레고를 조립하듯이 기본이 되는 기본 블럭이 있고거기에 특수한 기능을 가진 블럭을 붙이는 겁니다.예시// 형태type Shape = { style: string // 스타일 color: string // 색상}// 상세정보type Details = { name: string, // 이름 description: string, // 설명 date: number // 날짜}// 자동차 베이스type Car = { company: string, // 이름 country: string // 설명}만약 이러한 값이 있다고 하였을때자동차 베이스에 형태 + 상세정보를 합쳐야 하는 상황이 있는데지금과 같은 경우엔 나뉘어져 있어 조립이 필요한 상황입니다.이 경우 다음과 같이 하면 됩니다.// 형태interface Shape { style: string; // 스타일 color: string; // 색상}// 상세정보interface Details extends Shape { name: string; // 이름 description: string; // 설명 date: number; // 날짜}// 자동차 베이스interface Car extends Details { company: string; // 이름 country: string; // 설명}결과이렇게 할 경우 Car 타입을 사용하면 이런 형태를 지니게 됩니다.const myCar: Car = { style: "SUV", color: "red", name: "My Car", description: "This is a great car!", date: 2024, company: "Toyota", country: "Japan"};상속의 방법상속의 방법은 interface의 extends만 있는게 아닌데요 type에 &를 붙여 조합하는것 또한 가능합니다.그럼 결국 둘은 똑같은게 아니냐는 생각이 들수도 있을텐데비유로 다름을 설명해보자면const(상수)는 재선언 재할당이 불가능합니다.var(변수)는 재선언 재할당이 가능합니다.extends와 &는 이 차이인겁니다.interface의 extends는 재선언 재할당이 불가능합니다.type의 &는 재선언 재할당이 가능합니다.예시interface의 경우interface User { name: string;}interface User { age: number; // 병합됨}const user: User = { name: "CODi", age: 28,};type의 경우type User = { name: string;};type User = { // 오류 발생 age: number;};​​마무리이번에는 타입의 상속에 대해 알아보았습니다.그래도 블럭으로 비유하며 생각해보니 상속이라고 외우는것보다 더 쉽네요 ㅋㅋㅋ

썸네일
썸네일
썸네일
썸네일

NEW

4

/ 5

FSD 구조를 통해 프로젝트의 폴더 구조를 명확히 해보자

사진: Unsplash의 Leiada Krozjhen​개요항상 토이프로젝트를 만들다 보면 문득 고민에 빠지는 것들이 있었습니다.공용 컴포넌트의 경우, 이 컴포넌트는 여러 곳에서 사용되지만, 모든 곳에서 쓰지는 않습니다. 과연 여기 위치해도 괜찮은 것일까요?전역 사용 컴포넌트의 경우, 이 컴포넌트는 모든 곳에서 사용되긴 하는데, 하나의 컴포넌트 때문에 공용으로 사용하는 폴더에 넣는 것이 올바른 선택인지 고민됩니다.그 고민을 해결해보고자 이번에는 FSD에 대해 알아보고자 합니다.FSD 구조란​기능 단위로 나누어 개발하는 방식으로, 프로젝트의 복잡도가 증가할 때 관리와 확장성을 쉽게 해주는 아키텍처 패턴입니다.FSD의 핵심 개념은 각 기능이 독립적인 모듈로 구성된다는 것!즉, UI 컴포넌트, 상태 관리, 스타일링, 비즈니스 로직 등을 하나의 기능 단위로 묶어서 관리합니다.다음과 같은 이점들이 있습니다.1.모듈화: 각 기능은 독립적인 모듈로 분리되어 있기 때문에, 다른 기능에 영향을 주지 않고 개발 및 수정이 가능합니다.2.유지보수 용이성: 기능별로 코드를 관리하므로 특정 기능에 문제가 발생했을 때 쉽게 문제를 파악하고 해결할 수 있습니다.3.협업 효율성: 팀원들이 각 기능별로 작업을 나누어 진행할 수 있기 때문에 대규모 프로젝트에서 협업이 더 원활합니다.4.재사용성: 기능별로 모듈화된 코드를 다른 프로젝트나 페이지에서도 쉽게 재사용할 수 있습니다.구조 예시​/src /app # 앱 초기화 및 전역 설정 관련 (store, routing 등) /entities # 핵심 비즈니스 로직을 처리하는 객체들 (도메인 모델, 상태 등) /features # 개별 기능 단위 (서브 도메인) /pages # 페이지 구성 (라우팅된 주요 화면) /widgets # 페이지나 여러 기능에 걸쳐 재사용될 수 있는 UI 구성 요소들 /shared # 앱 전반에서 사용되는 공통 리소스 (유틸리티, 상수, API 함수 등)폴더 구조를 역할에 따라 나눈 후, 각 역할에 맞는 하위 폴더로 세분화합니다.예를 들어, 기능(feature)을 정리할 경우에는 다음과 같이 각 기능에 맞는 폴더를 구성합니다./features  /write /delete /like

썸네일
썸네일
썸네일
썸네일

NEW

5

/ 5

Google Analytics를 세팅하여 내 페이지 통계를 확인해보자!

개요본 웹사이트를 만들고 나니, 그동안 티스토리만 사용해왔던 탓에 통계를 어떻게 확인할지 고민이 생겨,여러 자료를 찾아본 끝에 ‘Google Analytics’를 통해 통계를 확인해보기로 결정했습니다.어떻게 되는걸까​요건 제가 예전에 등록했던 티스토리에 대한 통계인데요!만약 측정을 시작하면 이렇게 세세하게 내 페이지를 방문한 사용자들이나의 페이지를 어떻게 이용했는지, 어떻게 유입했는지 확인이 가능합니다.시작해보자​1. Google Analytics 방문Google Analytics를 방문하면 왼쪽 하단의 톱니바퀴를 눌러주세요!​2.  + 만들기 클릭왼쪽 상단 '+ 만들기' - '속성' 클릭3. 속성 이름 및 보고 시간대 선택속성 이름은 이 사이트를 구분할 이름(원하는 이름), 보고 시간대는 한국을 선택하시면 됩니다.4. 내 사이트 카테고리전 교육이나 취업에 관련된 내용에 치중되어있다 생각되어서 '취업 및 교육'으로 선택하였습니다.규모는 '작음'으로 선택하였습니다.5. 비즈니스 목표 선택모든 항목에 대한 통계를 보기위해 모두 선택 후 '만들기를 선택하였습니다'6.  head 태그 안에 Google Tag 관련 스크립트 추가만들기를 통해 나온 gtag 코드를 head태그에 삽입해주세요.예를 들어 아래 코드가 head 태그 안에 삽입 후 배포가 되어있어야Google Analytics가 접속해 확인이 가능합니다.<script> // <!-- Google tag (gtag.js) --> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-');</script>​​완료!하단과 같이 '데이터 수집이 활성화되었습니다.' 가 표시되었다면 세팅이 완료된거에요이제 사람들이 우리들의 사이트를 어떻게 훑어보고 있는지 확인해보면 될거같습니다,,,,ʕ◍·̀·́◍ʔฅ

썸네일
썸네일
썸네일
썸네일
썸네일

react

React Query에 대해 알아보자!

개요때는 1달전... 프로젝트를 진행하던 도중 특정 api의 호출을 통해 값의 여부에 따라 순차적으로 api를 호출해야할 일이 있었는데useEffect의 의존성 배열에 useState의 할당 여부를 기준으로 해결을 하다보니 그에따른 사이드 이펙트가 자꾸 생겨나해결 방법을 찾아보다 리액트 쿼리를 접하게 되었다......( °ᗝ° ).ᐟ.ᐟ​리액트 쿼리란?리액트 쿼리는 API로 fetch한 데이터의 로딩 상태, 에러, 받아온 값을 관리해주는 라이브러리 라고 합니다.또한 가장 중요한 점은 가져온 데이터를 캐싱하여 동일한 데이터에 대해 요청할 경우 메모리에 있는 데이터를 가져오는 기능을 제공합니다!!초기세팅다음과 같은 세팅이 필요합니다.// _app.jsimport { QueryClient, QueryClientProvider } from "@tanstack/react-query";import "@/assets/css/main.scss";const queryClient = new QueryClient();export default function App({ Component }) { return ( <QueryClientProvider client={queryClient}>     <Component {...pageProps} /> </QueryClientProvider> );}_app.js에서 이렇게 QueryClient를 인스턴스를 생성해 초기화를 하고QueryClientProvider로 감싸서 전역에서 작동되게 해야 합니다.어떻게 사용할까​axios를 통한 데이터를 fetch 하는 예시​​const [data, setData] = useState(null);const [error, setError] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => { const fetchData = async () => { try { const response = await axios.get("https://jsonplaceholder.typicode.com/posts"); setData(response.data); } catch (error) { setError(error.message); } finally { setLoading(false); } }; fetchData();}, []);if (loading) return <p>Loading...</p>;if (error) return <p>Error: {error}</p>;위 코드는 간단한 axios를 통한 데이터를 fetch 하는 예시인데보통 나도 그렇고 try catch 문을 통하여문제가 생겼을때는 에러 메세지를 표시해주고데이터를 받아오면 해당 데이터를 useState를 통해 받아온 값을 할당해 사용하는 방식을 취했었다.그럼 이제 React Query를 통해 fetch 하고 상태에 따라 어떻게 처리하는지 알아보자axios로 받아온 데이터를 fetch 하는 React Query로 관리하는 예시​import React from "react";import { useQuery } from "react-query";import axios from "axios";// 데이터를 받아오는 함수const fetchPosts = async () => { const response = await axios.get("https://jsonplaceholder.typicode.com/posts"); return response.data;};// 요소를 반환하는 컴포넌트const AxiosWithReactQuery = () => {​  // 여기서 React Query의 useQuery 훅을 통해 각각의 상태를 관리 합니다  const { data, error, isLoading } = useQuery({ queryKey: ["posts"], queryFn: async () => fetchPosts }); if (isLoading) return <p>Loading...</p>;         // 로딩이 true 일경우 Loading... 보여줌 if (error) return <p>Error: {error.message}</p>; // 에러가 true 일경우 error에 관한 메세지를 보여줌​  // 데이터 할당시 요소 반환 return ( <div> <h1>Data fetched with Axios & React Query</h1> <ul> {data.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> </div> );};export default AxiosWithReactQuery;전체적인 코드에 주석으로 간간히 적어놓긴 했지만 순서는 이렇다.1. useQuery의 첫번째 인자에는 키값, 두번째 인자에는 fetch를 실행할 함수* 키값은 꼭 각각 다르게 설정해주어 캐싱된 데이터를 사용할때 혼선이 생기지 않도록 해야합니다!const { data, error, isLoading } = useQuery({ queryKey: ["posts"], queryFn: async () => fetchPosts});2. 만약 fetch가 실행되면 isLoading이 true로 반환되어집니다.3-1. 로딩이 끝나고 데이터가 반환되었다면 data에 받아온 데이터가 할당됩니다.3-2. 로딩이 끝나고 데이터가 반환되지 못했다면 error에 error 메세지가 할당됩니다.저만의 활용예시​const { data: songList, isLoading: songListLoading } = useQuery({ queryKey: ["songList"], queryFn: async () => { const apiMain = new MainApi(); return await apiMain.getSongSearchList(utilArtist.getMainArtistList(playlist)); }, enabled: !!playlist, // playlist 데이터가 있을 때만 실행 });저의 경우 기존에는 useEffect를 통해 의존성 배열의 값이 재할당 될경우 api를 호출 후 useState의 변수명에 할당 하여 사용하는 방식으로 관리를 했었는데위와 같이 React Query의 enabled 옵션을 통해 playlist라는 변수에 값이 할당되어 있을때 fetch를 할수있도록 하였습니다.리액트 쿼리 덕분에 스케일이 커지면 커질수록 state, useEffect 등 남발하게 되어 관리가 힘들었는데 덕분에 손쉽게 관리하는 방법을 배웠네용 ୧(๑•̀⌄•́๑)૭참조​Tanstack Query​https://tanstack.com/query/latest​Json PlaceHolderhttps://jsonplaceholder.typicode.com/​

2024-10-20

썸네일

react

useMemo를 활용해 값이 그대로 들어올때 불필요한 실행을 방지해보자!

useMemo란?​React에서 제공하는 훅 중 하나로, 성능 최적화를 위해 사용되요기본적으로 useMemo는 값을 기억(memoization)해 불필요한 연산을 방지하는 역할을 담당하고 있어요.저의 경우엔 값은 재할당 되었으나 굳이 다시 렌더링 할 필요가 없는 작업이 있었는데 그 부분을 해결하기 위해 useMemo를 사용하였습니다!작동원리가 어떻게 되는걸까?// 함수 : 전달받은 파라미터에 2를 곱함const expensiveCalculation = (num) => { console.log("전달받은 파라미터에 2를 곱합니다."); return num * 2;};// 메인const MyComponent = ({ number }) => {    const result = useMemo(() => {        expensiveCalculation(number)    , [number]); return <div>Result: {result}</div>;};상단의 예시 코드를 보면매개변수 number의 값이 들어오면 useMemo에 의해 expensiveCaculation 함수가 실행되어그 결과가 result에 담겨 요소를 반환되어지고 렌더링시 result 값을 보여주는 컴포넌트에요useMemo는 첫번째 인자로 콜백함수를 받고 두번째 인자에는 의존성 배열을 받아요하단의 코드를 보면 매개변수 number의 값을 전달 받으면 useMemo에 의해 expensiveCaculation 함수를 실행되게 되는데​만약 useMemo의 의존성 배열 속 number가 다시 재할당 된다면 각각의 상황에 따라 콜백함수 실행 여부가 달라집니다. number에 같은 값이 재할당 되었을때 : expensiveCaculation 실행안함number에 이전과 다른 값이 재할당 되었을때 : expensiveCaculation 실행​이렇듯 useMemo는 의존성 배열의 값이 바뀌지 않으면 콜백함수를 실행하지 않아불필요한 계산을 방지하는 효과가 있고 이에 따라 성능 개선에 도움이 됩니다.

2024-10-14

썸네일

react

Vite + React 빠르게 시작해보기!

Vite?Vite는 프랑스어로 "빠르다(Quick)"를 의미하며, 발음은 "veet"와 비슷한 "비트" 입니다.)빠르고 간결한 모던 웹 프로젝트 개발 경험에 초점을 맞춰 탄생한 빌드 도구이며, 두 가지 컨셉을 중심으로 하고 있습니다.왜 사용하는걸까기술의 발전으로 인해 웹사이트 파일의 양이 많아지고 복잡해지면서, 두 가지 주요 문제점이 발생했어요,1.충돌과 오류 : 여러 개발자가 같은 서비스에서 작업할 때 변수나 함수 이름이 중복되어 충돌이 일어날 가능성이 높아져요.2.속도 이슈 : 대규모 웹사이트는 많은 자바스크립트 파일을 사용하므로, 파일 전송 속도가 느려지고 네트워크 병목현상이 발생할 수 있어요.이를 해결하기 위해 등장한 번들러는 여러 개의 파일을 하나로 묶어 전송 속도를 개선하고 충돌을 줄이는 역할을 하는데요,대표적인 번들러로는 Webpack, Parcel, Rollup이 있으며, 이는 가독성과 유지보수를 유지하면서도 성능을 최적화하는 방법이에요.그럼 무조건 Webpack을 사용하는것이 좋겠네?Webpack은 오픈 소스 자바스크립트 모듈 번들러로, 여러 개의 자바스크립트 파일을 하나로 압축하고 최적화하는 라이브러리에요.주된 장점으로는• 파일 압축 및 최적화 : 여러 개의 자바스크립트 파일을 하나로 묶어 네트워크 비용을 줄이고 로딩 속도를 개선할 수 있습니다.• 모듈화 개발 : 코드가 모듈 단위로 분리되어 가독성과 유지보수가 용이합니다.• 브라우저 호환성 : 최신 자바스크립트 문법을 지원하지 않는 브라우저에서도 사용할 수 있도록 코드 변환이 가능합니다.Webpack의 단점은 프로젝트 규모가 커질수록 빌드 속도가 느려진다는 점인데요,특히, 파일 수가 많아질수록 Hot Reload 기능을 통해 즉시 반영하는 과정이 매우 느려져, 때로는 빌드가 1분 이상 걸리기도 해 아무래도 1초라도 더 아껴야 하는 상황에선 큰 애로사항이죠그래서 나온 대안 Vite!Vite는 빠른 개발 환경을 제공하는 최신 자바스크립트 빌드 도구로, 기존의 번들러들과 비교해 빠른 개발 속도와 최적화를 특징으로 합니다.• 빠른 개발 속도 : Vite는 ESM(ECMAScript Modules) 방식을 사용해 로컬 개발 시 번들링을 하지 않고 소스 코드를 바로 제공하여 웹팩보다 20~30배 빠른 속도를 자랑합니다. 또한, Hot Module Replacement (HMR)를 지원해 개발 중 실시간으로 변경 사항을 즉시 확인할 수 있습니다.•. Rollup 기반 프로덕션 최적화 : 프로덕션 빌드에서는 Rollup을 사용해 *트리 쉐이킹(tree-shaking)과 같은 최적화 기술을 적용하여 불필요한 코드를 제거하고, 결과 파일 크기를 최소화해요.* 트리쉐이킹? : 마치 나무에서 불필요한 가지를 떼어내는 것처럼, 최종 결과물(번들)에서 사용되지 않는 코드를 잘라내어, 파일 크기를 줄이고 성능을 향상시키는 방식• 다양한 프레임워크 지원 : Vite는 React, Vue, Svelte 등 여러 프레임워크를 지원하며, 커스텀 플러그인으로 다른 프레임워크나 라이브러리와의 호환성도 제공해요.• 자산 처리 : CSS 모듈, SCSS, Less와 같은 프리프로세서, 이미지 등 다양한 자산을 처리할 수 있어요.이러한 기능 덕분에 Vite는 빠르고 효율적인 개발 환경을 제공하며, 특히 빠른 빌드와 HMR로 개발자에게 큰 장점을 제공해요.그럼 Vite 로 React 프로젝트를 만들어보자1. 버전 별 생성vscode를 켜서 다음과 같이 입력해주세요npm create vite@latest만약 특정 버전을 사용해야하는 경우npm create vite@5.4.02. 옵션 별 선택​저는 다음과 같이 하였습니다.어떠한 패키지가 설치되는지 확인 : y 입력 후 엔터프로젝트 이름(폴더명) 설정 : 원하는 이름 입력 후 엔터어떤 프레임워크로 만들건지 : 원하는 프레임워크로 방향키 위아래를 통해 설정하시면 됩니다!타입스크립트로 할지, 자바스크립트로 할지 설정 : 원하시는 걸로 선택하면 되나 SWC는 Rust로 작성되어 더 빠른 컴파일을 지원하지만 호환성에 문제가 발생 할 수 있습니다.다 완료가 되면 다음과 같이 폴더 구조가 완성 됩니다!3. 실행해보기현재 선택된 디렉토리를 변경하기 위해하단과 같이 입력하거나 해당 프로젝트의 폴더를 열어줍니다* 저는 test-project라는 이름으로 생성했기 때문에 test-project 폴더로 이동할게요cd test-project필요한 패키지를 설치하기 위해 다음과 같이 입력합니다.npm i* 만약 맥 이용자 중 오류가 발생했다면 권한 문제일수있으니 "sudo npm i"를 입력 후 본인 맥 비밀번호를 입력해 설치를 완료해줍니다.설치가 완료되었다면 다음과 같이 입력해 로컬서버에서 실행해줍니다.npm run devhttp://localhost:5174/ 부분을 다음과 같이 클릭해줍니다.윈도우 : 컨트롤(ctrl) + 클릭맥 : 커맨드(command) + 클릭아래와 같은 화면이 나오면 정상적으로 Vite를 이용한 프로젝트 환경이 갖춰졌습니다!​​마치며생각보다 어려운건 많이 없었습니다.앞으로 프로젝트를 만들때 Vite를 이용해 생성해보세요!

2024-10-13

썸네일

react

React에서 로딩중일때 임시로 표시할 스켈레톤 UI를 구현해보자!

리액트로 작업하던 토이 프로젝트가 있었는데API에서 데이터를 받아오는 동안 빈 화면으로 냅두는건 좀 아니다 싶어 스켈레톤 UI를 구현해보기로 했습니다!​스켈레톤 UI 활용 예시1. SCSS 작성우선 스켈레톤 UI 가 어느 상황에서든 잘 확장해 사용 할 수 있도록 flex를 사용해 CSS 구조를 잡았습니다.또한 스켈레톤 UI 가 표시되는 동안에 심심하지 않도록 'skeleton-loading' 이라는 로딩도 같이 넣어두었어요..skeleton-wrapper { width:100%; height:100%; display:flex; gap:2.0rem; > div { flex:1;        .skeleton { background:var(--color-gray-800); margin :10px 0; border-radius:4px; animation: skeleton-loading 1.5sinfiniteease-in-out;            &.text { width :100%; height :12px; }            &.title { width:50%; height:20px; margin-bottom:15px; }            &.avatar{ width:100px; height:100px; border-radius:0.8rem; }            &.thumbnail { width:100%; height:auto; aspect-ratio:1/1;/* 정사각형 비율 */ } } }}// 로딩을 위한 애니메이션@keyframes skeleton-loading {    0% {        background-color:var(--color-gray-800);    }    50% {        background-color:var(--color-gray-700);    }    100% {        background-color:var(--color-gray-800);    }}2. 구현에 사용할 JSX 구성2-1. Main.jsx : 상태 값에 따른 스켈레톤 UI 표시 여부 구현꼭 저와 똑같이 할 필요는 없기에 자신의 상황에 맞게 변형해 사용하시면 되요!일단 저는 설명을 위해 useState를 사용해 availiable 이라는 state의 기본값이 false 상태일 경우 SkeletonItem이라는 스켈레톤 컴포넌트를 표시하고availiable의 값이 true로 바뀔때는 원래 표시하려고 했던 내용들을 표시하도록 하였습니다.이때 중요한점은 로딩할때 보여줄 SkeletonItem의 내용은 원본과 비슷한 구조여야 합니다.그래야 로딩이 끝나고 이질감이 없겠죠?import React, { useState, useEffect, Fragment } from 'react';import SkeletonComponents from '@/components/common/SkeletonComponents';// 메인const main = () => {    const [availiable, setAvailiable] = useState(false);    return(        <Fragment>            { availiable ?                <div className="container">                    ​            <div className="container-inner">                        <h1>제목입니다</h1>                        <p>설명입니다.</p>                    </div>                </div>                : <SkeletonItem/>            }        </Fragment>    )}// 공통 : 스켈레톤 UIconst SkeletonItem = () => {    return (        <div className="skeleton-wrapper">            <div className="skeleton-playlist">                <SkeletonComponents type="title" />                <SkeletonComponents type="text" />            </div>        </div>    );};export default main2-2 SkeletonComponents.jsx : 타입별로 표시해줄 스켈레톤 컴포넌트 만들기scss를 작성할때 text, title 등등 타입별 스타일이 있었는데여기서 타입별로 props를 통해 해당하는 스타일을 이용할거기 때문에class명을 props로 받은 값을 이용 하도록 해주었습니다.const SkeletonComponents = ({ type }) => {    const classes=`skeleton ${type}`;    return <div className={classes} />;};export default SkeletonComponents;마치며이와 같은 과정을 거치면처음에 보여드렸던 예시와 똑같이 만들어졌을거에요간단하게만 보여드렸지만 좀 더 디테일한 구조는 한번 이것저것 하나씩 추가시켜 만들어 나가 보세요!

2024-10-13

헤더 영역 바로가기 컨텐츠 영역 바로가기 푸터 영역 바로가기