1. React / Next.js 관련 질문
React와 같은 SPA 라이브러리에 대한 내용
SPA와 MPA의 장단점을 설명할 수 있는가.
MAP : 사용자가 링크를 클릭할 때마다 서버에서 새로운 HTML 페이지를 받아오는 구조.
장점
- 모든 페이지가 서버에서 랜더링 되므로 검색 엔진이 쉽게 인덱싱 하여 SEO에 강함.
- 첫화면을 바로 서버에서 랜더링하여 사용자에게 전달하기에 초기 랜더링이 빠름.
- 페이지 단위로 서버에서 관리하여 보안이 유리하다.
단점
- 부드러운 페이지 전환이 불가능(깜빡임)
- 공통 컴포넌트를 매번 서버에서 다시 랜더링 해야한다.
- 클라이언트 측 인터랙션이 제한적으로 UX의 한계가 있다.
- 서버 요청이 많아서 속도가 상대적으로 느리다.
SPA : 단 하나의 HTML 문서를 기반으로 클라이언트에서 라우팅을 관리하는 구조
장점
- 빠른 페이지 전환 (클라이언트 라우팅 덕분).
- 풍부한 인터랙션과 UX 제공 가능.
- 서버와 클라이언트의 역할을 분리하기 용이 (REST API/GraphQL과 잘 맞음).
- 프론트엔드-백엔드 독립 배포 가능.
단점
- 초기 로딩이 느림 (JS 번들 크기 큼).
- SEO가 약함 (CSR만 쓰면 검색 엔진에 인덱싱이 어려움 → SSR로 보완 필요).
- 클라이언트에서 상태 관리가 복잡해짐.
정리하자면
"MPA는 서버 렌더링 기반이라 SEO와 초기 로딩 속도에 강점이 있지만, 페이지 전환이 느리고 UX가 떨어집니다. 반면 SPA는 빠른 페이지 전환과 풍부한 UX가 가능하지만, 초기 로딩과 SEO가 약해 SSR이나 코드 스플리팅으로 보완합니다.”
React와 같은 SPA 라이브러리가 필요한이유
- 빠른 페이지 전환을 통한 좋은 UX제공
- 페이지 새로고침X, 필요한 만큼만 부분 랜더링
- 페이지 전환이 부드러움.
- 컴포넌트 기반 개발
- UI를 컴포넌트 단위로 재사용 가능하게 만들었기에 개발속도가 빠르고 유지보수성 높음
- 아키택처 디자인 패턴에 따라 컴포넌트 및 서비스를 재사용하여 관리가 쉬워짐
- 백엔드- 프론트엔드 분리가 용이
- API 데이터만 받아서 클라이언트에서 뷰를 랜더링하기 때문에 백엔드와 프론트의 독립적 개발 및 배포가 가능하다.
- CSR/SSR/SSG 등 다양한 전략 가능.
- React Native, Electron 등과 기술적 연계가 가능하여 하나의 언어로 크로스 플랫폼 개발이 가능.
정리하자면
SPA 기반 라이브러리는 사용자 경험(빠른 전환, 네이티브 앱 같은 UX)을 크게 향상시키고, 컴포넌트 기반 아키텍처로 유지보수성과 재사용성이 높고 크로스 플랫폼 개발이 가능하며, 백엔드와 프론트엔드의 독립적 개발 및 배포가 가능하여서 입니다.
React의 장점과 단점.
- 리엑트는 프레임 워크가 아닌 라이브러리로 유연성이 높다.
- 컴포넌트 기반 철학의 선두주자로 레퍼런스가 많다.
- Virtual DOM을 통한 빠르고 효율적인 갱신.
React 컴포넌트 생명주기
생명주기(Lifecycle)이란 컴포넌트가 화면에 나타나고, 업데이트되고, 사라지는 과정을 뜻함. 함수형 컴포넌트에서는 useEffect를 통해 관리가 가능하다. useEffct안에 선언된 함수를 통해 마운트 시점 작업을 할 수 있으며 의존성 배열을 통해 업데이트 대상 값을 지정할 수 있고 (의존성 배열은 useEffect가 언제 실행될지를 결정하는 트리거로 UI를 변경하진 않는다.) return 문을 통해 언마운트시 작업을 정할 수 있다.
React의 렌더링 최적화 방법에 대한 내용
1. React.memo(컴포넌트 메모화)
컴포넌트를 메모이제이션하여, props가 변경되지 않으면 리렌더링을 방지한다.
const Child = React.memo(({ value }: { value: number }) => { console.log('렌더링됨'); return <div>{value}</div>; });
- 부모 컴포넌트가 자주 리렌더링되지만 자식의 props가 바뀌지 않는 경우.
- 비용이 큰 UI 렌더링 컴포넌트에서 사용( 나의 경우는 ReactQuill에 적용한 적 있음.)
- 얕은 복사로 props가 배열이나 객체인 경우에는 변경 되지 않음.
2. useCallback
function자체를 메모이제이션.const handleClick = useCallback(() => { console.log('클릭!'); }, []); <Button onClick={handleClick} />
handleClick이 리렌더링마다 새로 만들어지지 않고 같은 참조를 유지한다.
React.memo로 감싼 자식 컴포넌트에 함수를 props로 전달할 때 사용.
- 콜백을 dependency로 쓰는
useEffect등에서 불필요한 재실행 방지.
3. useMemo
값(Value)을 메모이제이션.
const expensiveValue = useMemo(() => { return heavyCalculation(data); // 비용이 큰 계산 }, [data]);
- 비용이 큰 계산(정렬, 필터링 등)**을 매 렌더마다 다시 하지 않게 하고 싶을 때 사용
- props로 전달되는 객체/배열을 새로 생성하지 않고 유지하고 싶을 때 사용.
React.memo → 컴포넌트 자체를 메모 (props 변하지 않으면 리렌더링 방지).
4. 상태 관리 최소화
상태관리 최소화란 정말 필요한 곳에만 State를 두고 나머지는 계산된 값 으로 처리하는 방법
- State를 실제로 사용하는 하위 컴포넌트로 내려서(localize) 리렌더링 범위를 줄인다.
- 기존 props나 state로 계산 가능한 값은 state로 두지 않는다.
- Context의 남용 방지
- 컴포넌트 분리 + memoization
정리하자면
상태관리 초소화는 State를 정말 필요한 컴포넌트에만 두고, 파생 값은 계산으로 처리하며, 전역 상태를 최소화 하는 것을 의미합니다. 이는 리렌더링 범위를 줄이고 성능을 최적화 할 수 있습니다.
5. Virtualization (가상 스크롤)
만약 Api를 통해 수천 수백개의 데이터를 받아오고 해당 데이터를 통해 리스트나 테이블을 구현할때 브라우저에게 부담을 덜어 주기 위한 전략
가상 스크롤이란 - 모든 리스트 아이템을 한 번에 DOM에 렌더링하지 않고, 화면(Viewport)에 보이는 영역만 DOM에 렌더링하는 기술. - 스크롤이 이동할 때 보이지 않는 영역의 DOM은 제거하고, 새로 보이는 부분만 렌더링한다. - 대표 라이브러리:react-window,react-virtualized. - 예시 :MUI DataGrid
주의사항
- 아이템 높이가 유동적이면 성능이 떨어질 수 있어 → 보통 고정 높이를 권장.
- 무한 스크롤(infinite scroll)과 같이 사용할 때는 Intersection Observer를 조합.
- 너무 많은 상태나 복잡한 컴포넌트를 넣으면 렌더링 부하가 발생할 수 있어 →
React.memo와 함께 최적화 필요.
정리하자면
가상 스크롤은 대량의 데이터를 표시할 때 화면에 보이는 부분만 DOM으로 렌더링하는 최적화 기법입니다.
단 대용량의 데이터를 불러오는 것 자체도 속도 지연이 발생할 수 있기에 Api페이지네이션 처리를 하면 더욱 효율적입니다.6. key 관리 & 재조정(Reconciliation)
재조정(Reconciliation) 이란
React에서 State나 Props가 변경되면 새롭게 생긴 VirtualDOM과 이전 VirtualDOM을 비교하여 변경된 부분만 실제 DOM에 반영하는 비교 & 반영 과정을 뜻 한다.Key 란 동일한 컴포넌트를 반복적으로 랜더링 할때 각 요소를 구분하기 위한 유일한 식별자다. key를 올바르게 선언하지 않으면 React는 요소를 index 기반으로 추적하게 되어, 중간 요소가 추가/ 삭제 되면 불필요한 리렌더링이 일이어난다. 인덱스를 key로 쓰면 요소 순서가 바뀌는 순간 React가 요소를 잘못 인식해 불필요한 리렌더링과 상태 초기화 문제가 발생할 수 있다. 그래서 고유 ID를 key로 사용하는 게 안전하다.
키를 통한 재조정 과정
- React는 동일한 key를 가진 요소끼리 비교해서, 변경 여부를 확인한다.
- key가 다르면 기존 DOM을 제거하고 새로 생성한다.
- key가 같으면 기존 DOM을 재사용하고 속성(props)만 업데이트한다.
7. Lazy Loading 와 Code Splitting
Lazy Loading(지연로딩)은 필요한 시점에만 리소스를 로드하는 기법으로 초기 페이지 로딩 시 모든 js파일을 하번에 불러오는 대신, 사용자가 특정 기능을 요청할 때 해당 기능의 js 번들을 불러오는 방식이다.
import React, { lazy, Suspense } from "react"; const MyComponent = lazy(() => import("./MyComponent")); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <MyComponent /> </Suspense> ); }
- 초기 로딩 속도 개선 (필요 없는 컴포넌트는 나중에 로딩).
- 사용자가 방문하지 않는 페이지는 JS 로딩을 아예 안 함.
Code Splitting (코드 분할)은
js 번들을 여러 개의 작은 청크로 나눠서 필요할 때만 로드하는 기법이다.
vite와 같은 빌드 도구에서 import를 만나면 자동으로 코드 스플리팅을 한다.//import() 덕분에 MyComponent 관련 코드가 별도 JS 청크로 분리됨.. const MyComponent = lazy(() => import('./MyComponent'));
Lazy Loading vs Code Splitting
- Code Splitting: 코드를 "쪼개는" 빌드 단계 개념. (번들 최적화)
- Lazy Loading: 쪼갠 코드를 필요할 때 로드하는 실행 단계 개념.
Lazy Loading 와 Code Splitting 관계
SPA의 초기 로딩 성능을 개선하기 위해 Code Splitting으로 JS 번들을 나누고, Lazy Loading으로 실제 필요한 시점에 해당 번들을 불러와 렌더링합니다. React에서는React.lazy와Suspense가 대표적인 구현 방식이다."
///about 페이지를 들어가야 About 컴포넌트를 네트워크에서 가져온다. const About = lazy(() => import("./pages/About")); <Routes> <Route path="/about" element={<About />} /> </Routes>
8. 브라우저 API 최적화
브라우저 API란
웹 브라우저가 제공하는 기능을 조작할 수 있는 인터페이스(함수/객체) 집합으로
자바스크립트 자체에는 없는 기능인데, 브라우저가 JS 엔진(V8 등) 위에서 동작하면서 제공해주는 추가 기능.이미 내가 잘알고 있는 브라우저 API는 아래와 같은 것들이 있다.
- document.querySelector() : DOM을 조작하기 위한 API
- localStorage.setItem() : 브라우저 로컬 스토리지에 값을 저장하기위한 함수
- fetch() : HTTP 파이프라인을 구성하는 요청과 응답 등의 요소를 조작하는 함수.
- addEventListener() : 브라우저에서 발생하는 이밴트를 등록하기 위한 함수.
브라우저 API는 JavaScript의 내장 함수(Built-in Object)가 아니라, 브라우저가 JS 엔진 위에서 웹을 제어할 수 있도록 제공하는 추가 기능이다.
React와 브라우저 API 최적화의 상관 관계
브라우저 API 최적화와React 렌더링 최적화는 리렌더링은 최소화 + 브라우저 작업 부하도 최소화”라는 공통 목표를 갖고 있다.
requestAnimationFrame
Debounce & Throttle
Intersection Observer
Passive Event Listener
React 대표 라이브러리에 대한 내용
1. React Hook Form을 쓴 이유와 useForm의 동작 원리.
React-hook-form은 여러 입력 컴포넌트를 하나의 State로 연결하면 입력을 할때 마다 모든 컴포넌트가 리렌더링 되어 불필요한 랜더링 비용이 커지기에 사용한다. React-hook-form은 인풋 컴포넌트들을 비제어 컴포넌트 기반으로 연결하여 실제로 변한 필드만 업데이트하고 필요할 때만 React에 알리는 형태다.
추가로
필수입력 , 패턴 , 최소 글자수 등 검증 옵션을 제공한다.
만약 비제어 컴포넌트에 변경사항을 감지하고 싶으면 watch() 를 사용한다.
나의 경우 shadcn과 Mui 컴포넌트를 사용하기 위해 기존의 register가 아닌 control 객체를 사용하였다.
useForm의 동작 원리는 다음과 같다.
- 폼 컨트롤러 초기화(필드 상태, 폼상태 초기화)
- register를 통한 input에 ref를 연결해 DOM과 이벤트를 추적
- input이 변경할 경우 RHF의 내부 proxt객채만 관리하고 React 컴포넌트에는 영향을 주지않음.
- 폼이 제출될때 ref로 연결된 모든 input을 읽어 JS객체로 변환
2. Tanstack Query(react-query)에 대한 내용
Tanstack Query란
비동기 서버 상태를 관리하는 라이브러리로 Api 로딩 상태, 데이터 캐싱, 재요청, 페이지네이션 등 매번 직접 구현해야 하는 것을 TanStackQuery를 통해 자동화 할수 있다. 이는 UX적인 관점에서도 사용자의 대기시간을 동일 Api 기준 제거할 수 있어 좋으며 개발 단계에서도 많은 역할을 할 수 있다.
useQuery
- 데이터를 조회하고 캐싱하는 역할.
- 캐싱/ 리패칭 / 상태 관리를 할 수 있다.
- 컴포넌트가 마운트되면 자동 실행 가능하다.
useMutation
- 서버에 데이터 생성, 수정, 삭제 할 때 사용.
useQuery와 다르게 자동 실행이 아닌 직접 호출.
- 성공시 캐시를 무효화 하여 서버 상태 최신화 가능.
- 성공/ 실패 콜백을 쉽게 관리 가능하다.(retry 숫자, 또는 함수로 커스터마이징 가능)
API 호출 실패 시 재시도 로직을 어떻게 구성했는가?
나는 TanstackQuery는 데이터 관리의 역할만 부여하고 싶어서 재시도는 HTTP 함수의 인스턴스에서 HTTP 응답코드에 따라 작동하게 하여 선호하지 않지만, 시도하려면 retry옵션을 통해 가능하다.
3.전역 상태관리 라이브러리에 대한 내용.
주로 사용한건 ContextApi와 useReducer를 사용하여 전역 상태를 관리했는데.
도메인별로 전역상태를 나누다보니 Provider를 중첩해서 선언해야하는 번거로움이 있어 전문 라이브러리중 Redux를 사용해보았다.
Redux의 흐름은 store를 구성하고 액션-리듀서를 통해 store를 갱신하는 흐름을 보았다
Next.js에 대한 내용.
1. SSR과 CSR의 차이
CSR (Client-Side Rendering)
브라우저가 빈 HTML을 받고 JS를 다운로드 후 실행해 화면을 그림.
초기 서버 부하가 적음, 클라이언트 중심으로 동적 인터랙션이 유리하지만
초기 로딩이 느리고 SEO에 취약하다.
SSR (Server-Side Rendering)
서버에서 HTML을 완성한 뒤 클라이언트로 전달
초기 렌더링이 빠름, SEO에 유리하지만
서버 리소스를 더 많이 사용, 서버 응답 속도에 영향.
2. App Router란 무엇인가
2. TypeScript와 JavaScript 기본
JavaScript 기본기
1. 클로저
클로저는 함수가 선언될 당시의 스코프(변수 환경)를 기억하고, 그 스코프 밖에서 호출되더라도 접근할 수 있는 함수를 의미한다. 즉, 함수가 만들어질 때의 렉시컬 환경(변수 범위)를 캡쳐해서 기억한다는 내용.
클로저의 필요성은 다음과 같다.
- 데이터 은닉 : 함수 외부에서 직접 접근하지 못하는 변수를 관리
- 상태 유지 : 한 번 호출 후에도 변수를 유지하여 누적 계산 같은 작업을 수행할 수 있음.
클로저의 주의할 점
- 메모리 누수 가능성 : 클로저로 참조된 변수가 계속 메모리에 남아 있음.
- 필요없는 클로저는 즉시 해제하거나, 내부 변수를 null로 초기화 해야함.
실제로 클로저를 개발자가 주의해야 할 경우
- 컴포넌트 안에서 등록한 이밴트 리스너는 언마운트 시 제거
- 컴포넌트 안에서 사용한 브라우저 Api는 언마운트 시 제거
- 만약 비동기 작업중 사용자가 해당 컴포넌트를 언마운트 시킬 상황이 고려된다면 마운트 플레그를 만들어 state에 set하는 행위를 방어해야함.
useEffect(() => { let isMounted = true; const fetchData = async () => { const res = await fetch('/api/huge-data'); const data = await res.json(); if (isMounted) setData(data); }; fetchData(); return () => { isMounted = false; }; }, []);
Arrow Function
ArrowFunction 화살표함수는
화살표 함수는function키워드 대신 ⇒ 문법으로 작성되는 간결한 함수 표현식으로, 자신의this,arguments,super를 갖지 않고 상위 스코프의 값을 렉시컬하게 바인딩 하는 함수이다.
화살표 함수 주 사용 목적
- 콜백함수(이벤트 핸들러, Promise, Array매서드 등)
- this가 필요 없거나 상위 스코프로 사용할 때.
- 이밴트 핸들러에서는 이벤트를 발생시킨 DOM요소를 가르킨다.
- 간결하게 함수를 선언하고 싶을때(Hooks 내부 콜백 등)
ES6에서 가장 자주 쓰는 문법.
- 변수선언 (const let)
- 화살표 함수
- 템플릿 리터럴(`${}`)
- 구조분해할당
- 스프레드 연산자 / rest파라미터
- 디폴트 파라미터
- export/import
- promise & async/await
Promise와 async/await 차이 및 이벤트 루프 동작 원리.
Promise란
비동기 작업의 성공/실패를 표현하는 객체로, 미래에 완료될 작업을 약속한다..then(),.catch(),.finally()로 결과를 처리한다. Promise 객체는 resolve와 reject로 각 성공 실패 결과를 반환한다.
async/await
Promise 기반 비동기 코드를 마치 동기 코드처럼 작성할 수 있는 문법이다. async 함수는 항상 Promise를 반환하며, await 키워드는 Promise가 처리될때까지 기다렸다가 결과를 반환한다. async 함수는 then/catch 체인 없이 try…catch로 오류를 처리할 수 있어 가독성이 좋다.
await Promise.all
두개 이상의 async/await 구조를 기다려야하는 직렬 구조라면 Promise.all을 통한 병렬 처리도 가능하다.const [a, b] = await Promise.all([ fetch("/api/a"), fetch("/api/b") ]);
호이스팅(hoisting)
호이스팅이란
변수나 함수 선언이 코드 실행 전에메모리의 최상단으로 끌어올려진 것처럼 동작하는 JS의 특징.
TDZ (Temporal Dead Zone, 임시적 사각지대)
변수가 선언은 되었지만 초기화되기 전에 접근하려고 하면 참조 에러(ReferenceError)가 발생하는 구간.
console.log(b); // ReferenceError let b = 5; sayHello(); // OK function sayHello() { console.log('Hello'); } sayHello(); // TypeError const sayHello = () => console.log('Hello');
특징 | var | let / const |
스코프 | 함수 스코프(Function Scope) | 블록 스코프(Block Scope) |
호이스팅 | 선언 + undefined 초기화 | 선언만 호이스팅 (초기화 X) |
TDZ 발생 여부 | X | O (TDZ 존재) |
재선언 가능 여부 | O | X |
재할당 가능 여부 | O | let: O / const: X |
콜백 함수가 무엇인지
콜백함수
다른 함수에 인자로 전달되어, 특정 시점에 호출되는 함수를 말한다. 비동기 로직이나 이벤트 처리에 자주 쓰인다. 이러한 콜백 함수는 중첩되다 보면 가독성이 떨어지는 콜백지옥이 생기는 문제가 있다. 이를 해결하기 위해 Promise와 async/await이 등장했다.
TypeScript 기본기
정적 타입 시스템(TypeScript)의 장점과 any를 최소화하는 방법.
type과 interface의 차이
type과 interface 모두 객체 구조를 정의하거나 타입을 선언할 때 비슷하게 사용할 수 있지만,
미묘한 차이와 용도가 있다.
interface
객체 중심 설계, 라이브러리 확장/병합이 필요한 경우 유리하다.
extends키워드로 확장이 가능하다.
- 동일한 인터페이스 명으로 여러 번 선언하면 자동으로 합쳐진다.
type
유니온, 교차, 조건부 타입 등 고급 타입정의가 필요할 때 유리함.
유니온(|)또는교차(&)연산자를 통해 복잡한 타입 조합이 가능하다.
- 동일한 이름으로 중복 선언이 불가하다.
- primitive 타입(기본타입) 별칭도 가능하다.
interface는 객체 구조 정의와 확장에 특화되고 선언 병합이 가능하며, type은 유니온, 교차 타입 등 복잡한 타입 조합에 유리합니다. 객체 타입만 정의한다면 interface, 조합이 필요하다면 type을 사용합니다.
3. 브라우저와 통신
브라우저의 렌더링과정
- HTML 문서를 파싱하여 DOM트리를 만든다.
- CSS파일/스타일을 파싱하여 CSSOM트리를 만든다.
- DOM+CSSOM를 합쳐 렌더 트리를 만든다.
- 스크립트 태그를 만나면 DOM파싱을 멈추고 JS를 실행한다.
- Layout과 Paint 과정을 거쳐 화면을 그린다.
브라우저는 HTML을 DOM으로, CSS를 CSSOM으로 파싱하고 이 둘을 합쳐 렌더 트리를 만든 뒤 Layout과 Paint 과정을 거쳐 화면에 그립니다. 중간에 <script>를 만나면 JS 실행을 위해 DOM 파싱이 잠시 멈춥니다.주소창에 도메인을 입력시 일어나는 일들
- 해당 URL을 캐시(브라우저,OS 등)에 해당 도메인IP가 있는지 확인
- 없다면 DNS 서버에 요청을 하여 IP주소 확인
- 3-way-handshake 를 통해 TCP소켓연결 https 라면 TLS/SSL handshake로 인증서 교환 및 암호화 통신셋팅
- 브라우저가 HTTP프로토콜로 요청 메시지 전송
- 서버가 응답으로 정적리소스를 반환
- 브라우저가 랜더링 처리.
도메인을 입력하면 브라우저는 캐시를 확인하고, DNS 조회로 IP를 찾은 후 TCP/TLS 연결을 설정합니다. 이후 서버에 HTTP 요청을 보내고 응답을 받아 DOM/CSSOM 파싱 및 렌더링 과정을 거쳐 화면을 표시합니다.
http와 https의 차이에 대해 설명
- HTTP (HyperText Transfer Protocol)
- 웹에서 데이터를 주고받기 위한 기본 프로토콜.
- 암호화되지 않은 평문 통신을 사용.
- HTTPS (HTTP Secure)
- HTTP + SSL/TLS 암호화 계층이 추가된 프로토콜.
- 데이터 전송 시 암호화·무결성·인증을 보장.
HTTP는 평문 통신으로 보안에 취약하지만, HTTPS는 SSL/TLS로 데이터를 암호화해 보안, 무결성, 인증을 보장합니다.
HTTP method를 설명하기
HTTP 메서드는
클라이언트가 서버에 어떤 동작(행위)을 요청하는지 나타내는 방식으로 RESTful API를 설계하거나 사용할 때 반드시 알아야 하는 핵심 개념이다.
메서드는 멱등성(같은 요청을 여러번 보내도 서버의 상태가 동일), 안전성, 캐싱 가능성을 가진다.GET
- 목적 : 서버에서 리소스 조회
- 데이터가 URL 쿼리 파라미터에 포함됨.
- 데이터를 가져오는 것만 가능 (리소스 변경 X)
- 안전성, 멱등성, 캐시가 가능하다.
POST
- 목적 : 서버에 새로운 리소스 생성
- 요청 Body에 데이터를 담아 전송.
- 서버의 상태나 DB를 변경.
- 캐싱되지 않음.
PUT
- 목적 : 리소스 전체를 업데이트(덮어쓰기)
- 요청 Body에 전체 리소스를 담아야함.
- 리소스가 존재하지 않으면 새로 생성하는 의미를 가짐.
PATCH
- 목적 : 리소스 일부만 업데이트
- PUT과 달리 부분 수정만 진행
DELETE
- 목적 : 리소스 삭제
HTTP 메서드는 서버에 대한 요청 동작을 정의하며, GET(조회), POST(생성), PUT(전체 수정), PATCH(부분 수정), DELETE(삭제)가 가장 많이 쓰입니다.
HTTP Polling/SSE
Websocket 설명하기
WebWorkder / serviceWorker
CORS와 브라우저 보안 정책 관련 질문 (CSRF vs XSS 차이).
쿠키, 세션, 웹스토리지의 차이에 대해 설명
4. 구현경험 질문
Quill 에디터 커스터마이징: 어떤 점이 어려웠고 어떻게 양방향 호환성을 확보했는가?
두 가지 기억이 있다.
- 지라와 ReactQuill이 서로 호환이 되게 하기위해 함수를 만들어야 하는데 간단한 h1 태그 부터 시작해서 테이블을과 같은 조합된 태그들을 마크다운 문자열을 재 조립하고 테스트하는 과정이 생각했던 것 만큼 쉽지 않았다.
- ReactQuill에 있는 도구 박스가 지라에 있는 도구박스에 비해 부족해서 커스텀 하는 과정에 여러가지 코드가 섞여서 나중에 유지관리를 위해 코드 스플릿하는 과정에서 기준 잡기가 애매했다. 결과 적으로는 주석 처리만으로 도구 툴을 추가 제거 할 수 있게 분리하는 것에 성공했었다.
JWT 인증 시 액세스 토큰과 리프레시 토큰 관리 전략 설명.
토큰 발급 및 저장
- 로그인 시도시 서버에서 액세스 토큰과 리프레시 토큰+ 토큰의 만료시간을 전달함.
- 리프레시 토큰은 HttpOnly 쿠기에 저장, 액세스 토큰은 메모리에 저장.
- 전역 상태로 토큰의 만료 시간을 관리.
토큰 검증 및 라우팅
- URL 최초 접근시 쿠키에 리프레시 토큰이 없다면 로그인 페이지로 리다이렉트
- 리프레시 토큰이 존재하면 서버에 새로운 액세스 토큰 발급 요청 진행.
- 라우터 프로텍터를 통해서 전역상태 관리된 토큰 만료시간이 현제보다 과거 거나 빈 문자열이면 모든 라우팅을 로그인 페이지로 이동.
API 호출
- 모든 API 요청 헤더에 액세스 토큰을 전달.
- Axios 인터셉터에서 엑세스 토큰의 만료가 감지되면 엑세스 토큰 재발급 Api 호출
토큰 만료처리
- 리프레시 토큰 만료 후 라우팅 시도를 하면 로그이페이지로 이동
로그아웃 처리
- 서버에 리프레시 토큰 폐기요청.
- 쿠키에 저장된 리프레시 토큰 삭제.
LCP 49% 향상은 어떤 방식으로 측정하고 개선했는지 구체적으로 설명.
우선 기본으로 주어진 비가시성 이미지를 랜더링했을때 LCP가 대략 1,8초에 가까운 시간이 걸리는것을 개발자 도구함으로 확인하였습니다. 이는 눈으로 볼때도 이미지가 끊기면서 랜더링되는 정도의 채감을 가지고 있었습니다.
하여 비가시성 워터마크의 생성 및 검증 방식을 우선 확인하여 반복 패턴인것을 확인하여
1개의 패턴의 비가시성 워터마크를 CDN으로 받아 Z-index가 가장 높은 요소에 이미지 반복 패턴 처리를 하였고, 검출 가능한 비율 유지를 위해 최적의 이미지 사이즈 및 투명도 작업을 진행하였습니다.
DevTools 탐지 및 차단 모듈은 어떤 원리로 이미지 삭제를 막았는지?
브라우저 렌더링 최적화를 위해 렌더링 순서를 어떻게 제한했는지?
WebSocket 기반 UX 개선 사례(모달 단계 시각화) 구체적으로 설명.
비가시성 워터마크 갱신 로직을 구현할 때 고려한 보안 포인트는?
Notion API 기반 블로그 프로젝트에서 SSR/CSR 전략.
5.협업 과정
Git 사용
Notion 또는 Slack 사용경험
6. 개인 질문
자기소개
안녕하세요.
저는 프론트엔드 개발자에 지원한 김태헌입니다.
저는 이전 직장 마크애니에서 기술지원과 솔루션 웹 모듈 개발 및 유지보수를 담당했습니다.
주요 성과로는 기존 솔루션의 제약적인 문제를
웹 모듈 확장 개발로 개선해 고객사 확보에 기여했고,
내부 관리자 페이지를 개발해 기술 지원 및 영업 정보를 일관되게 관리할 수 있는 환경
을 구축하여 프로젝트 관리 효율을 높였습니다.
이러한 과정에서 저는
사용자 관점의 UX가 중요하다는 것을 깨달아, 프로세스 단축·일관된 UI 적용·사용자 피드백 반영
을 중점으로 작업했습니다.
저는 이러한 경험을 기반으로, 특히 대시보드처럼 복잡한 데이터 시각화와 성능 최적화가 필요한 UI개발에 강한 관심이 있어 OOO에 지원하게 되었습니다.
커리어 관련 질문(수행 ⇒ 프론트엔드 개발자)
처음 개발을 시작한 계기는 내부 연구소에서 고객 요구를 반영하기 어려운 상황이어서, 직접 웹 모듈을 개선하며 해결해본 경험이었습니다. 그 과정에서 프론트엔드의 매력과 가능성을 느껴 집중적으로 개선 작업을 진행했지만, 현재 환경에서는 더 깊은 성장을 하기 어렵다고 판단해 전문적인 역량을 키우기 위해 이직을 결심했습니다.
협업& 커뮤니케이션 관련
백엔드 개발자와의 협업
- Swagger를 활용하여 Api 명세를 확인하였다.
- JWT와 같은 상호간 규칙이 필요한 설계는 같이 진행하였다.
- HTTP응답코드 및 URL 구조는 주도적으로 진행하였다.
디자이너와 협업
- 화면설계 단계에 참여하여 어떤 UI 아이탬을 사용할지(모달 또는 드로우월) 예시를 전달과 함께 의견을 전달하였다.
- 피그마에서는 괜찮겠지만 실제로 데이터가 들어가면 의도와 멀어질것 같은 디자인에 대한 피드백을 위해 Mocking데이터를 구성하여 storyBook으로 피드백을 전달하였다.
- 디자인 가이드를 위해 컬러 파레트 폰트 가이드 등을 작성하고, TailwindCSS의 특징을 디자이너와 공유했다.
비개발 부서와의 협업
- PM에게 하드코딩이 된 MVP를 보여주며 구현 형태를 보여주고 비지니스 로직을 추가하였고 그 과정에서 필요한 추가 요구사항을 반영하여 지속적인 피드백을 진행했다.
- 가급적 기술적으로 어려운 용어를 사용하지 않기위해 노력했음.
성장방향과 목표
지금 당장은 어떤 개발자냐고 묻는다면 react 개발자라고 대답할것 같다.
하지만 많은 경험과 지속적인 학습을 통해 완벽한 프론트엔드 개발자가 되어서
좋은 개발 문화를 가진 프론트엔드 팀의 리더가 되고 싶다.
예를 들면 레퍼런스를 공유하고 새로운 프로젝트 도입시 팀원들의 성장을 돕기위해 각자 관심있는 도메인 기술을 해볼수 있게 기획하고 새로운 라이브러리 도입에 의논하는 등을 꿈꾸고 있다.
퇴사 및 이직 이유
퇴사를 결심한 이유는 제 커리어 방향성과 회사 방향성이 점점 달라졌기 때문입니다.
당시 부서는 유지보수 위주로만 진행되었지만, 저는 프론트엔드 전문성을 쌓고 최신 기술을 활용해 시장 경쟁력을 가질 수 있는 제품화를 계속 시도했습니다. 그러나 부서 차원에서 새로운 제품화 추진은 이루어지지 않았고, 결국 저는 팀 이동을 요청했지만 받아들여지지 않았습니다.
그래서 제 성장을 위해 새로운 도전을 선택하게 되었고, 앞으로는 회사의 방향과 제 성장 목표가 잘 맞아 장기적으로 기여할 수 있는 환경에서 일하고 싶습니다.
왜 프론트엔드를 선택했는가
저는 처음에 ‘어떤 기능을 만드는 개발자’가 되고 싶다는 목표를 가졌습니다.
그리고 그것을 사용자에게 직접 전달하기 위해서는 프론트엔드가 필수적이라 프론트를 시작했습니다.
개발을 해보니 단순히 기능 구현을 넘어서, 퀄리티와 UX가 제품의 인상을 크게 좌우한다는 것을 깨달았고, 그 과정에서 사용자 관점에 자연스럽게 관심을 가지게 되었습니다.
현재는 프론트엔드에 전문성을 쌓는 데 집중하고 있지만, 장기적으로는 백엔드와 전체 서비스 구조까지 이해하여
기획 단계에서 의사결정에 참여하고, 프론트와 백엔드를 아우르는 그림을 그릴 수 있는 개발자로 성장하고 싶습니다.