HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🚗
⭐ 현대 소프티어 부트캠프 2조 - 카북 ⭐
/
👥
개발 일지
/
🥰
프론트엔드
🥰

프론트엔드

 
개발 환경 세팅
💡
개발 환경 세팅

기술 스택

  • 언어 : TypeScript
  • 스타일 : SCSS
  • 통신 : Axios
  • 번들러 : Vite
  • 배포 : Netlify
  • 코드 formatter : prettier, eslint

세팅 과정

 
1. vite를 통한 ts 프로젝트 세팅
npm create vite@latest carbook -- --vanilla-ts
 
  1. scss, axios 설치
npm i sass npm i axios
 
  1. eslint/prettier 설정
https://seonghui.github.io/blog/2020/12/27/typescript-eslint-prettier/
 
  • Ts 파일을 import 하는 과정에서 경로를 잘못 입력하여 제대로 불러오지 못하는 문제가 있었지만, 경로를 제대로 입력하여 해결하였다.
 
절대 경로 설정
  • tsconfig.json
{ "compilerOptions": { ... "baseUrl": "./", "paths": { "@": ["src"], "@/*":["src/*"] }, }, "include": ["src"], }
  • vite.config.ts
    • import { defineConfig } from 'vite'; import * as path from 'path'; export default () => defineConfig({ resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, });
    • 절대경로가 잘 적용 됐는지 테스트를 해보려고 했는데 .ts 확장자를 인식 못하는 오류가 있었다.
    • 스택오버플로우를 찾아보니 package.json에서 type=”module”을 지우라고 해서 지웠는데 이번엔 import, export를 못한다고 오류가 났다.
    • 한참을 해매고 있었는데 은아님의 조언덕분에 해결했다.
    • node run dev(npm rum vite)명령어를 이용해서 테스트를 했어야했는데
      • ts-node 명령어를 이용해서 node 환경에서 테스트를 하려고 해서 오류가 났다…
여러개의 파일을를 하나의 index.ts로 export 하기
https://nyagm.tistory.com/157
axios 객체 만들기
  • 모든 파일들에서 사용할 axios 객체를 만들었다
  • api/index.js
    • export { axiosInstance } from './axiosInstance';
  • api/axiosInstance.ts
    • import axios from 'axios'; // fake 서버에 테스트 하기 위한 임시 url const BASEURL = 'https://reqres.in'; export const axiosInstance = axios.create({ withCredentials: true, baseURL: BASEURL, });
    • withCredentials 옵션:
      • cors를 해결하기 위해서 적용
      • 클라이언트에서 api 통신을 요청할때 쿠키나 인증헤더등의 옵션을 포함해서 전송하려면 true로 설정해야한다. 링크

axiosInstance 테스트

  • reqres라는 fake server를 이용한 axios 테스트 예제가 있길래 axiosInstance를 이용해 따라해보았다
  • 갑자기 cors 오류가 생겨서 생각보다 오래걸렸다
  • axios객체의 withCredentials을 true로 해놓은 것이 원인이었다.
  • api/axiosInstance.ts
import axios from 'axios'; const BASEURL = 'https://reqres.in'; export const axiosInstance = axios.create({ // fake 서버에 요청하므로 인증정보 안보냄, 지금 true로하면 cors 에러 // withCredentials: true, baseURL: BASEURL, });

App.ts

import { Component } from '@/core'; export default class App extends Component { template(): string { return `<div> <input type="email" placeholder="email을 입력해주세용" id="email" value="" /> </div> <div> <input type="password" placeholder="비밀번호를 입력해주세용" id="pw" value="" /> </div> <input type="button" class ='button' value="로그인" />`; } }

main.ts

import App from '@/App'; import { axiosInstance } from '@/api'; const app = document.querySelector('#app') as HTMLElement; new App(app); const onLoggin = async () => { const email = document.getElementById('email') as HTMLInputElement; const password = document.getElementById('pw') as HTMLInputElement; try { // axios으로 로그인 요청 (post) let res = await axiosInstance.post('/api/login', { email: email.value, password: password.value, }); console.log(res); document.write(JSON.stringify(res)); } catch (err) { console.log(err); throw new Error((err = 'error')); } }; const btn = document.querySelector('.button'); btn?.addEventListener('click', onLoggin);

예제 로그인 폼

notion image

로그인 성공 시

notion image

로그인 실패 시

notion image
history Api를 이용한 라우팅 기능
참고 링크
https://nukw0n-dev.tistory.com/34
https://github.com/KimKwon/frontend-basic/tree/main/content/history/vanilla-router/src
 
해당 링크에서는 js로 구현하여 function을 이용했는데
현 프로젝트에서는 ts로하다보니 new로 인스턴스를 생성하는 과정에서 타입 에러가 나서 해결 방법을 찾아보다 function에서 new로 생성했을 때 타입 에러를 해결하는 방법보다 class형태로 선언하는 게 더 편리한 것 같아 class형태로 변경하였다.
https://stackoverflow.com/questions/43623461/new-expression-whose-target-lacks-a-construct-signature-in-typescript
 
그리고 navigate라는 메서드 보다 push, replace가 더 익숙한 편이라 따로 함수로 분리해주었다.
 
+ router 기능을 utils에 넣은 이유는 모든 컴포넌트에서 공통적으로 쓰일 수 있는 모듈이기에 utils로 분리했고, 아쉬운 점은 findMatchedRoute에서 routes를 매개변수로 받는 형태로 한다면 좀 더 범용적인 클래스가 될 것 같다고 느낀다.
 
추가로 customEvent에 대한 type을 어떻게 처리해야되는가를 많이 찾아봤는데
Argument of type '(e: CustomEvent) => void' is not assignable to parameter of type 'EventListenerOrEventListenerObject'
This is due to the behavior of the --strictFunctionTypes compiler flag added in TypeScript v2.6. A function of type (e: CustomEvent) => void is no longer considered to be a valid instance of EventListener, which takes an Event parameter, not a CustomEvent. So one way to fix it is to turn off --strictFunctionTypes.
Argument of type '(e: CustomEvent) => void' is not assignable to parameter of type 'EventListenerOrEventListenerObject'
https://stackoverflow.com/questions/47166369/argument-of-type-e-customevent-void-is-not-assignable-to-parameter-of-ty
Argument of type '(e: CustomEvent) => void' is not assignable to parameter of type 'EventListenerOrEventListenerObject'
event 자체는 Event란 타입으로 지정해두고, e.detail을 가져올 때 e에다가 제네릭을 사용하여 customEvent타입을 붙여주었다니 오류가 해결되었다.
무한스크롤 구현
VanillaJs로 무한 스크롤 구현하기(feat: Intersection Observer)
모바일 UI에서 많이 보이는 무한 스크롤! ex) 페이스북, 인스타 등 해당 UI를 구현하기 위해서는 크게 두 가지 방법이 있다. 1. 스크롤 이벤트 이용 2. Intersection Observer 이용 필자는 observer api를 이용하여 구현할 예정이다. 스크롤 이벤트를 이용하여 구현하는 방식은, 매번 window의 스크롤 이벤트를 감지하여 스크롤 위치가 컨텐츠의 끝에 닿았는지 확인하는 절차가 필요하기에 성능 상 좋지 않다고 한다.
VanillaJs로 무한 스크롤 구현하기(feat: Intersection Observer)
https://aeunhi99.tistory.com/321
VanillaJs로 무한 스크롤 구현하기(feat: Intersection Observer)
무한스크롤 최적화
마지막 요소를 관찰 시 api 를 호출하여 새로운 돔요소를 추가하는 방식은 돔 요소를 무한정으로 증가하기 때문에 dom 요소가 증가함에 따라 브라우저가 버벅이는 현상이 발생할 수 있다.
 
무한스크롤을 최적화 하는 방법을 찾아보았는데
첫번째 방법은 페이스북/인스타 그램의 최적화 방식이다.
페이스북의 무한스크롤 최적화 방식은 가상 스크롤 방식을 이용하는데, 현재 페이지에서만 보이는 것만 렌더링하고 보이지 않는 요소에 대해서는 제거하는 방식이다.
이 방법을 착안하여 단순히 안보이는 요소에 대해 display:none을 하면 어떨까 했는데
이 방법 또한 display:none이 됨에 따라 요소들이 렌더링 되지 않으면서 모든 요소들의 위치가 바뀜에 따라 reflow및 paint 작업이 다시 일어난다. 그래서 할 수 없다..
오늘의집 내 무한 스크롤 개발기 - 오늘의집 블로그
무한 스크롤 적용 시 발생하는 문제점을 오늘의집 개발팀에서는 어떻게 해결했을까?
오늘의집 내 무한 스크롤 개발기 - 오늘의집 블로그
https://www.bucketplace.com/post/2020-09-10-오늘의집-내-무한스크롤-개발기/
오늘의집 내 무한 스크롤 개발기 - 오늘의집 블로그
무한 스크롤 최적화에 대해 더 찾아보다가 오늘의 집이 무한 스크롤 개발기 라는 글을 발견했는데
어떠한 방식으로 가상 스크롤을 구현한지 알 수 있었다. 근데 너무 너무 너무 너무 복잡핟..
근데 이제 오늘의 집은 컨텐츠 길이가 다 달라서 그것을 계산하는 로직때문에 복잡한거지 우리 프로젝트는 이미지 크기가 고정되어있어서 계산하는 로직이 필요하지 않을 수도 있다…
가상 스크롤을 구현할 것인가….
아니면 다른 방식이 있다. 최적화라고 할 수있는지는 모르겠지만
스크롤을 내리다가 돔 개수가 일정 개수 이상 커지면 더이상 불러오지 않고 “추가 로드하기” 버튼을 만드는 것이다! 그 버튼을 누르면 이전 요소들이 삭제되고, 새로운 요소들이 새로 불러와 진다.
이전 요소들은 클라이언트상에서 저장하고 있고, 사용자가 위로 스크롤할 때 “이전 데이터 로드하기’ 버튼을 누르면 이 후 요소들이 삭제되고 이전 요소들이 다시 불러오는 방식이다.
 

로그인 기능

세션 쿠키 vs 웹 스토리지(localStorage, sessionStorage)
https://inpa.tistory.com/entry/JS-📚-localStorage-sessionStorage