Q1. "브라우저의 '이벤트 위임(Event Delegation)'이란 무엇인가요? 왜 사용하는가요?"
컴포넌트 간 데이터 전달과 기존 이벤트 위임을 섞어서 대답을 해버렸다..하하..;;
내 대답 : 자식 컴포넌트에서 작동해야할 이벤트가 부모 컴포넌트로 위임되어 사용되는 기법입니다. 예를 들어 부모 컴포넌트에서 자식 컴포넌트로 이벤트나 값이 상속되는것은 바로 내려서 사용할 수 있지만. 반대로 자식 컴포넌트에서 부모 컴포넌트로 값을 위임하고 원하는 이벤트를 실행시키기 위해서는 콜백함수로서의 이벤트 위임이 필요하게 됩니다. 이벤트 위임이 필요하지 않을경우 preventDefault로 이벤트 위임을 막을 수 있습니다.
리스트 같은 아이템을 클릭할 경우 하위 요소마다 일일히 이벤트 함수를 등록하지 않아도 부모 상위 요소에서 이벤트 함수를 등록하여 인덱스 요소에 맞게 사용하게 되면 성능을 향상시킬 수 있게 됩니다.
피드백 : 아이고야 두 개념이 섞여있는데요? 컴포넌트 간 데이터 전달(상속, 콜백 함수 전달)이랑 이벤트 위임 두가지가 있군요. 부모 -> 자식, 자식 -> 부모 콜백 전달 흐름은 리액트 컴포넌트 통식 방식이고 제가 질문드린 의도는 브라우저 DOM이벤트 처리 기법입니다.
Tip!
이벤트 위임 이란?
왜 쓰나?
1) 퍼포먼스 향상
// HTML
<ul id="menu">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
// JS
document.getElementById('menu').addEventListener('click', function (event) {
if (event.target.tagName === 'LI') {
console.log('You clicked:', event.target.textContent);
}
});
답 : 브라우저의 이벤트 위임은 이벤트 버블링을 활용하여, 하위 요소마다 이벤트 핸들러를 등록하지 않고 상위 요소 하나에 이벤트 핸들러를 등록하는 방식입니다. 이를 통해 성능을 향상시킬 수 있고, 동적으로 추가되는 DOM요소도 처리할 수 있어 실무에서도 리스트, 테이블, 카드 UI등에서 자주 사용하게 됩니다.
여기서 든 궁금증, 리액트는 그럼 이런 이벤트 위임과 관련하여 리액트 자체적 매커니즘이 있다는 소리인건데 어떻게 처리를 하는건지 궁금하였다.
질문1. 리액트는 어떻게 이벤트를 처리하나요?
리액트는 리액트 자체의 이벤트 위임 매커니즘에 의존한겁니다. 아까 질문의 의도는 DOM 이벤트 위임은 브라우저의 기본 동작이라고 보시면 되구요. 리액트는 내부적 자동으로 이벤트 위임을 하고 있다는게 포인트. 리액트는 SyntheticEvent라는 가짜 이벤트 시스템을 씁니다. 그리고 이거를 위해 최상단에서 하나의 이벤트 리스너만 등록하고 모든 하위 컴포넌트의 이벤트를 버블링으로 감지해서 처리하죠. 궁금하신게 아래같은 코드겠죠?
<ul>
<MyItem />
<MyItem />
</ul>
// MyItem 컴포넌트 내부
function MyItem() {
return <li onClick={() => console.log('clicked')}>item</li>;
}
자 질문의 의도가 리액트같은 코드에선 li를 컴포넌트로 뺐을때, li에서 onClick을 주는게 일반적으로 사용하는 방법인데 질문의 답변은 ul에서 이벤트 위임하여 쓰는게 성능적으로 좋다고 해놓곤 여기서는 왜 이렇게 쓰느냐는 거겠죠? :)
겉보기엔 li에 이벤트 핸들러가 달린것처럼 보여도요 사실 리액트는 내부적으로 root요소에 한번만 이벤트 등록하고 li에서 클릭이 발생하면 그걸 위로 버블링해서 잡아내고 처리하는 겁니다.
차이점
브라우저 DOM이벤트 위임은 직접 상위 DOM 요소에 등록해야하고 브라우저의 이벤트 버블링을 사용하게 됨. 성능 최적화/동적 요소 대응을 하기 위한것이고 개발자가 직접 구현해야함
리액트 이벤트 처리는 리액트 내부에서 root에 자동으로 한번만 등록하고 SynctheticEvent로 버블링을 흉내내게 됩니다. 성능 최적화/추상화된 이벤트 처리를 위한 것이며 리액트가 자동으로 처리를 하게 됩니다.
그러면 이런식으로 질문이 들어올 수 도 있겠죠? 관련해서 여러가지 질문을 해볼게요
Q2. 리액트에서는 이벤트 위임을 어떻게 처리하나요?
내 답변 : 리액트 내부적으로 상단 root에서 이벤트를 한번만 등록을 한뒤 SyntheticEvent로 버블링을 흉내내어 이벤트를 처리하게 됩니다.
거의 맞았습니다 다만 버블링을 흉내낸다기 보다는 추상화하여 일관된 이벤트 객체를 제공한다라는 말로 정정하는게 좋겠네요. 상단 root도 정확히 실제 DOM인지 리액트 루트인건지도 짚어주세요.
모범답변 : 리액트는 최상위 DOM(root)에 단 한번만 이벤트 리스너를 등록합니다. 내부적으로 SyntheticEvent라는 자체 이벤트 시스템을 사용해, 브라우저 이벤트 버블링을 추상화하고 일관된 방식으로 이벤트를 처리합니다.
Q3. 리액트의 이벤트 시스템은 브라우저의 이벤트 위임과 어떤 차이가 있나요?
내 답변 : 리액트는 내부적으로 이벤트 시스템을 등록하여 사용한다는 점. 시스템 브라우저의 이벤트 위임은 직접 개발자가 상위 요소의 DOM에 이벤트 요소를 등록하여 사용해야 된다는 점의 차이가 존재합니다.
문장이 조금 난해합니다. 시스템 시스템의 연속의 중복이 있고 핵심은 리액트는 자체 이벤트 시스템을 제공해 개발자가 일일이 DOM에 이벤트를 등록할 필요가 없다. 라는걸 명확히 해야죠
모범답변 : 브라우저의 이벤트 위임은 개발자가 직접 상위 DOM에 이벤트를 등록해야하지만, 리액트는 자체 SyntheticEvent 시스템을 통해 자동으로 최상위 요소에 이벤트를 등록하고 관리합니다.
Q4. 왜 리액트는 모든 이벤트를 root에 한번만 등록할까요?
내 답변 : 성능 최적화를 위해서입니다. 메모리 관점으로도 한번만 상위요소에서 등록을 하게 된다면 사용할때마다 개발자 입장에서도 이벤트 버블링을 해줄 필요가 없기 때문에 편리하게 사용할 수 있게 됩니다.
답변이 두서가 없고 "이벤트 버블링을 해줄 필요가 없다"?는 표현이 이상합니다. 버블링은 브라우저가 하는거고 개발자가 직접 하는건 아니잖아요? 대신 메모리 사용량 감소와 이벤트 관리 일원화가 주 이유라고 보시면 됩니다.
모범답변 : 이벤트를 최상위 root에 한번만 등록하면 메모리 사용을 줄이고, 이벤트 처리를 일원화하여 성능과 유지보수성을 향상시킬 수 있습니다.
Q5. 리스트에서 각각의 아이템에 이벤트를 등록할 때, li마다 onClick을 다는것과 ul에 onClick을 한번만 다는 것의 차이는 뭔가요?
내 답변 : li마다 onClick을 하게 되면 리액트와 같은 라이브러리나 프레임워크를 사용하지 않는 이상 많은 비용이 발생하게 됩니다. 반면에 ul에 onClick을 한번만 달 경우 비용의 감소를 하여 성능적으로 이점이 생기게 됩니다. 다만 li의 인덱스를 받아 분기처리를 해줘야하는 차이점이 존재하게 됩니다..
순수 JS라면 비용 차이가 크지만 이게 만약 리액트 관점이 된다면 내부적으로 이벤트 위임을 이미 사용하기 때문에 Li마다 이벤트를 걸어도 브라우저에 여러 리스너가 등록되지 않아 비용 차이가 크지 않게 됩니다. 리액트에서는 주로 편리함과 가독성을 위해서라고 보시면 됩니다.
모범 답변 : 순수 자바스크립트에서는 각 li에 이벤트를 등록하면 비용이 증가하지만, 리액트 내부적으로 이벤트 위임을 사용하기 때문에 li마다 이벤트를 걸어도 비용 차이가 크지 않습니다. 다만 ul에 한번 이벤트를 걸면 이벤트 대상에 따라 분기 처리를 해야합니다.
Q6. 이벤트 버블링이 항상 일어나나요? 멈출 수는 없나요?
내 답변 : stopPropagation을 사용하게 되면 이벤트 버블링을 막을 수 있게 됩니다..
간결해서 좋네요. 근데 조금만 더 부연설명을 붙여 볼까요?
모범 답변 : 대부분의 이벤트는 버블링하지만 event.stopPropagation()을 호출하면 해당 이벤트의 버블링을 중단할 수 있습니다.
Q7. 이벤트 위임을 사용할 때 주의할 점이 있나요?
내 답변 : 이벤트 위임이 필요하지 않을 경우 적절한 stopPropagation을 사용하는것이 중요하고 하위 요소에서 필요한 경우 해당 하위 요소의 DOM을 적절하게 파악하는 것이 중요합니다..
문장이 너무 길고 너무 두서가 없어요. 이벤트 위임이 필요하지 않을 경우? 말이 이상한데요 핵심은 이벤트 위임 시 이벤트 대상을 정확히 식별하고 불필요한 이벤트 전파를 막는게 중요하다. 라는거죠
모범 답변 : 이벤트 위임 시에는 이벤트가 실제 발생한 요소를 정확히 구분해야 하며, 불필요한 이벤트 전파를 막기 위해 상황에 따라 stopPropagation()을 적절히 사용해야합니다.
Q8. 이벤트 캡처링과 버블링의 차이는 뭔가요?
내 답변 : 이벤트 캡처링은 이벤트 버블링과 반대로 부모 요소에서 자식요소로 이벤트 요소를 전파 시킬때 사용하는 기법입니다..
핵심은 맞긴한데 너무 단순해서 마이너스 요인이 될거 같아요. 캡처링은 이벤트가 타겟에 도달하기 전에 상위 요소에부터 내려오는 단계이고, 버블링은 타켓에서부터 상위요소로 올라가는 단계라는 점을 짚어야 합니다.
모범 답변 : 이벤트 캡처링은 이벤트가 상위 요소에서 하위 요소로 내려가는 과정이고, 버블링은 이벤트가 하위 요소에서 상위 요소로 올라가는 과정입니다. 대부분의 이벤트는 버블링 단계에서 처리됩니다.
개념 이해는 기본적인 베이스는 되어 있지만, 문장이 두서가 없고 핵심 전달이 안되는 느낌이에요. 면접답변은 간결하면서 핵심을 정확히 짚는것이 중요합니다. 그리고 이건 중요한데 실무 경험이나 예시가 들어가야 아, 이 부분에 대해서 다루어 보았구나 하고 실무 감각을 어필 할 수 있게 됩니다.
오늘의 리마인드 면접 질문
Q1. localStorage, sessionStorage, cookie의 차이를 설명해보세요.
내 답변 : 로컬 스토리지는 사용자가 직접적으로 삭제하지 않는 이상 페이지를 종료하여도 살아있는 데이터 저장소입니다. 일반적으로 로그인시 아이디 저장과 같은 기능을 사용할 때 사용하게 됩니다. 세션스토리지는 로컬 스토리지와는 조금 다르게 창을 종료하게 되면 데이터가 휘발되는 저장소입니다. 대신 탭별로 개별의 스토리지를 갖고 있습니다. 용량적으로는 로컬 스토리지와 세션스토리지는 5~10mb가량의 용량을 지니고 있으며 쿠키는 4kb정도의 작은 용량을 가지고 있다는 점도 차이점으로 들 수 있습니다. 쿠키는 앞선 스토리지와는 조금 다른 특징을 가지고 있습니다. httpOnly의 특징을 가지고 있어 xss와 같은 보안적으로 우수하게 사용할 수 있습니다. 자바스크립트 코드로 직접적으로 접근할 수 없고 서버단에서 head에 포함되어 사용하게 되어 일반적으로 로그인과 같은 보안/인증 인가에서 사용하게 됩니다.
httpOnly는 쿠키를 설정할 때 옵션이라는걸 정확히 짚을 필요가 있는데요 모든 쿠키가 사실 httpOnly인건 아니고 쿠키는 서버가 응답시 Set-Cookie 헤더로 설정할 수 있고, HttpOnly옵션이 붙은 경우에는 JS로 접근할 수 없어 보안에 유리합니다.라고 답하면 더 좋겠죠? 그리고 이건 실무적인 부분인데 쿠키는 요청마다 자동전송되기 때문에 네트워크 트래픽 부담이 될 수 있어요. 그래서 요즘은 JWT저장 방식에서 cookie보다 LocalStorage를 쓰는 경우도 많습니다. 대신 보안에 좀더 신경을 써야겠죠? 그리고 좀더 면접관입장에서 이해하기 쉽게 단계적으로 설명하는 부분이 필요해보여요
모범 답변 : 로컬스토리지, 세션 스토리지, 쿠키는 모두 클라이언트 측 저장소이지만 보관기간, 보안성, 서버 전송 여부에서 차이가 있습니다.
로컬 스토리지는 탭을 닫아도 유지되며 자바스크립트로 접근 가능하고 주로 사용자 설정이나 캐싱된 데이터를 저장할 때 사용합니다.
세션 스토리지는 탭이 닫히면 사라지며, 로그인 직후의 임시 상태값 저장 등에서 유용합니다.
쿠키는 용량이 작지만 서버 요청 마다 자동으로 전송되며 httpOnly, Secure같은 옵션을 통해 보안성이 높아 인증 토큰 저장등에 사용됩니다.
각각의 저장소는 유지 기간, 보안, 서버 연동 여부를 기준으로 목적에 맞게 선택하는 것이 중요합니다.
Tip!
로컬스토리지 - 지속되는 사용자 설정
ex) 아이디 저장, 다크모드, 최근 본 상품 목록
세션스토리지 - 세션(탭)단위의 임시 상태 저장
ex) 로그인 직후 리다이렉트용 임시 URL (로그인 성공 후 가려던 페이지로 이동), 폼 입력 중 새로고침 대비(작성중인 내용 복원), 페이지 간 탐색 중 임시 검색 필터 유지
쿠키 - 보안이 필요한 인증/인가 정보 저장
ex)로그인 시 발급받은 AccessToken/RefreshToken 저장(HttpOnly, Secure 옵션으로 XSS 방어), 서버 사이드 렌더링 시 사용자 인증 정보 자동 전달(서버에서 바로바로 전달), 장바구니, 최근 본 상품 등 서버에서도 동기화가 필요한 상태(쿠키로 저장시 서버에서도 파악 가능)
Q2. localStorage와 cookie 중에서 로그인 토큰 저장 시 어떤 것을 선택하겠어요? 그리고 그 이유는?
내 답변 : 쿠키를 선택할 것 같습니다. 쿠키는 서버에서 접근가능한 스토리지로서 AccessToken을 갖고 있고 httponly와 같은 특징을 지니고 있기에 크로스사이트스크립트와 같은 보안적인 문제에서 자바스크립트 코드가 직접적으로 접근하여 코드를 조작시킬 수 있는 문제를 미리 방지할 수 있게 됩니다. 일반적으로 이러한 인증/인가 문제를 고려하여 로그인 시에는 쿠키를 많이 사용하게 됩니다. 하지만 네트워크 트래픽이 많은 페이지라면 쿠키는 요청이 있을 경우 자동으로 서버로 요청을 하기에 서버 부하가 걸릴 우려가 존재하게 됩니다. 보안적인 문제는 추가적으로 고려를 해야한다는 점이 존재하나 이와같은 예외적인 상황에서는 로컬스토리지를 사용할 것 같습니다.
AccessToken을 쿠키에 넣는 건 위험할 수 있습니다. 일반적으로는 RefreshToken을 쿠키에, AccessToken은 메모리나 localStorage에 보관하는 방식이 많고 이유는 access는 짧고 refresh는 재발급용이기 때문이죠. 그리고 마지막 문장 “보안적인 문제는 추가적으로 고려해야 한다는 점이 존재하나” → 문장 구조가 조금 모호합니다. 좀 더 명확한 조건부 설명이 좋을 듯! “자바스크립트 코드가 직접적으로 접근하여 코드를 조작시킬 수 있는 문제” → ‘토큰 탈취’라는 표현이 더 정확해.
모범 답변 : 로그인 토큰 저장 시 저는 쿠키를 선택하겠습니다.
쿠키는 서버에서 접근 가능한 스토리지로, HttpOnly 옵션을 설정하면 JavaScript로 접근할 수 없어 XSS 공격에 안전하다는 장점이 있습니다.
인증/인가 처리가 중요한 페이지에서는 HttpOnly, Secure, SameSite 옵션이 설정된 쿠키를 통해 refresh token을 관리하고, access token은 메모리 혹은 localStorage에 저장하는 방식도 자주 사용됩니다.
다만 쿠키는 모든 요청마다 서버에 자동으로 전송되기 때문에, 트래픽이 많은 서비스에서는 불필요한 쿠키 전송으로 인해 서버 부하가 발생할 수 있습니다.
이런 경우 보안 수준과 시스템 특성에 따라 localStorage를 선택하기도 합니다.
저도 이전 프로젝트에서 인증 처리 방식에 대해 고민하며, 쿠키 보안 옵션을 적용해 XSS를 방어하고 동시에 네트워크 부하를 줄이기 위한 전략을 사용한 경험이 있습니다.
면접 대비는 정말 파도파도 끝이 없다. 알던 내용이라 생각해도 베이스에 그치면 안되고 내가 "활용한 경험"을 언급하는게 너무너무 중요하다. 또한 이거하면 이거 식으로 단순하게 대답하려는 습관도 고쳐야겠다. 면접관 입장에서도 단순히 이건 이거다 라고 답하는 식은 오히려 마이너스 요인이 될것이기 때문에.. 오늘은 그런점을 고려하여 이벤트 위임에 대해서 좀더 집중적으로 공부하였다.
내일도 힘내자!