어제는 JavaScript Promise와 React관련하여 기술면접의 주제를 잡고 질의응답을 진행하였다..
조금 여러므로 지친 관계로 어제는 블로그를 작성하지 못하였지만.. 취준에 핑계를 댈 수는 없으니 마음가짐 다잡고 다시 해보자
다행히도 얼추 해당 내용에 대한 내용이나 방향성에 대해서는 알고 있던 터라 내가 알고 있던 내용의 보충이나 추가적인 질문과 실무에서의 코드 활용에 대해서 좀더 집중적인 질의를 갖고 꺠달았던거 같다!
그렇기에 문장의 흐름이나 조금 다듬는 방향으로 피드백보다는 추가적인 팁을 추가로 작성하였다.
Q1. Promise.all 과 Promise.race의 차이를 설명하고, 실무에서 언제 각각을 사용하면 좋을까요?
A. Promise.all은 여러 개의 비동기 작업을 병렬로 실행하고 모든 작업이 성공해야 결과를 반환합니다. 하나라도 실패하면 전체가 실패로 간주되며 catch 블록으로 넘어가게 됩니다. 실행시간은 가장 오래 걸리는 작업을 기준으로 합니다.
실무에서는 게시글 목록 페이지를 예시로 들 수 있습니다. 게시글을 불러오는 list API와 게시글 마다의 좋아요 수, 유저 정보를 불러오는 API를 모두 불러와야 게시글 페이지를 정상적으로 유저에게 보여줄 수 있기 때문에 이 경우에는 Promise.all을 사용하는 것이 좋습니다.
Promise.race는 여러 비동기 작업 중 가장 먼저 완료된 작업의 결과만 반환하게 됩니다. 여기서 "완료"'란 성공이든 실패든 상관없이 settled 상태를 뜻합니다. 실행시간은 가장 빠른 비동기 작업의 시간만큼만 소요됩니다.
실무에서는 여러 이미지 서버에 요청해서 가장 빨리 온 이미지를 우선 보여주는 경우에 유용하며 타임아웃 처리에서도 Promise.race형탤 자주 사용됩니다. 또한 자동 완성 검색 기능에서 여러 요청 중 가장 빠르게 완료된 결과를 사용하는 상황에도 적합합니다.
추가 tip!
Q2. Promise.all과 Promise.allSettled의 차이는 무엇인가요? 각각의 사용 시나리오에 대해 설명해주세요.
A. Promise.all은 여러 개의 비도익 작업을 병렬로 실행하고 모든 작업이 성공해야 결과를 반환합니다. 하나라도 실패하면 전체가 실패로 간주되며 catch블록으로 넘어가게 됩니다. 실행시간은 가장 오래 걸리는 작업을 기준으로 합니다.
실무에서는 게시글 목록 페이지를 예시로 들 수 있습니다. 게시글을 불러오는 list API와 게시글마다의 좋아요 수, 유저 정보를 불러오는 API를 모두 불러와야 게시글 페이지를 정상적으로 유저에게 보여줄 수 있기 떄문에 이 경우에는 Promise.all을 사용하는것이 좋습니다.
Promise.race는 여러 비동기 작업 중 가장 먼저 완료된 작업의 결과만 반환하게 됩니다. 여기서 "완료"란 성공이든 실패든 상관없이 settled상채를 뜻합니다. 실행시간은 가장 빠른 비동기 작업의 시간만큼만 소요됩니다.
실무에서는 여러 이미지 서버에 요청해서 가장 빨리 온 이미지를 우선 보여주는 경우에 유용하며, 타임아웃 처리에서도 Promise.race형태로 자주 사용됩니다. 또한 자동완성 검색 기능에서 여러 요청 중 가장 빠르게 완료된 결과를 사용하는 상황에도 적합합니다.
추가 tip!
Q3. 브라우저에서 발생하는 CORS(Cross-Origin Resource Sharing)에 대해 설명해보세요.
A: CORS는 Cross-Origin Resource Sharing의 약자로, 브라우저가 보안상의 이유로 다른 출처(Origin)의 리소스 요청을 제한하는 정책입니다.
예를 들어 웹사이트 주소가 https://example.com인데 https://api.riotgames.com과 같은 외부 API 요청을 보내면 브라우저에서 이를 차단하게 됩니다.
왜 필요한가요?
브라우저의 보안 정책으로, 악의적인 사이트가 사용자 모르게 다른 사이트의 데이터를 요청하는 것을 방지하기 위함입니다.
어떤 상황에서 CORS 에러가 발생하나요?
현재 사용 중인 도메인의 주소가 아닌 외부 API를 불러왔을 경우에 발생합니다.
프론트엔드에서 어떻게 해결할 수 있을까요?
서버 측 해결: 응답 서버에 Access-Control-Allow-Origin 등의 CORS 헤더를 설정
프론트엔드 해결: 프록시 서버를 활용
저는 Glitch를 사용해서 프록시 서버를 만들어 외부 API를 중계하는 방식으로 우회한 적이 있습니다. 실제로 Riot API를 사용할 때 CORS 문제가 발생했고, 이를 통해 해결한 경험이 있습니다.
또한 Vercel을 통해 Next.js 프로젝트를 배포할 때, /api 디렉토리를 활용한 Serverless Function을 프록시 서버로 사용해서 CORS 문제를 해결해본 경험도 있습니다.
+) 여기서 들어온 추가 질문
면접관이 추가로 물을 수 있는 Follow-up 질문
순간 너무 멍했다..ㅠ 그렇기에 일단은 기록기록..
1) CORS와 JSONP의 차이는?
2) CORS는 클라이언트 측에서 해결할 수 없나요?
3) Preflight 요청이 뭔가요?
4) 브라우저가 왜 이런 정책을 강제할까요?
Q4. React에서 상태 관리 시 useState와 useReducer의 차이점은 무엇인가요? 그리고 각각 언제 사용하는 것이 적합한지 설명해 주세요.
A. useState는 간단한 상태 관리에 유용합니다. 예를 들어 사용자가 버튼을 클릭했는지에 대한 여부를 판별하는 것과 같은 간단한 로직, 토글이나 입력값 관리에 적합합니다.
useReducer는 여러 가지 액션 타입에 따라 복잡한 상태 전환이 필요할 때 사용하는 것이 좋습니다. 예를 들어 버튼을 클릭했을 때 해당 버튼이 삭제 버튼인지 수정 버튼인지에 따라 dispatch를 받아 해당 액션 타입에 따른 반환값을 반환해줄 수 있게 됩니다.
추가 tip!
마지막 이 부분은 react의 상태 관리와 밀접하게 연관된 질문이기에 좀더 세분화하여 다루는것이 좋다. 라는 피드백을 받았기에 조금 개선할 여지가 있어 보인다.
Q5. React에서 상태를 변경할 때 왜 직접 상태를 수정하지 않고 setState(또는 useState의 setter)를 사용해야 할까요? 직접 상태를 변경하면 어떤 문제가 발생하나요?
내 답변 : 리액트는 이전값과 현재 값을 비교하여 변경할 부분만 부분적으로 업데이트를 하는 가상 DOM을 만든 뒤 dom을 내보내는 값으로 리렌더링을 하게 됩니다. 하지만 setState가 아닌 현재 값 자체를 수정하게 된다면 이전 값을 덮어쓰게 되어 현재 값의 수정된것인지를 판별할 수 없게 되버립니다.
피드백 :
중요한 핵심이 다 들어가 있고, 말하는 방식도 면접에서 자연스럽게 자기 말로 설명하는 느낌이라 진짜 잘 대답하신 답변입니다. 근데… 조금 더 논리 흐름이 “왜 그렇게 되는지 → 그래서 어떻게 해야 하는지” 순으로 딱 잡히면 더 깔끔해지는데요.
예를 들면,
React의 상태 업데이트 방식을 먼저 간단히 소개
: React는 상태가 변경될 때마다 컴포넌트를 리렌더링하면서 UI를 최신 상태로 유지하게 됩니다.
->
이때 상태 변경을 React가 인식하려면 어떤 조건이 필요한지 설명
: 이때 상태가 "진짜로 바뀌었는지"는 이전 상태와 새 상태를 얕은 비교(shallow compare)로 판단합니다.
->
직접 수정하면 어떤 문제가 발생하는지 언급
: 그런데 객체나 배열을 직접 수정하면 참조가 바뀌지 않기 때문에 React는 변경을 감지하지 못해 리렌더링이 일어나지 않게 됩니다.
->
그래서 어떻게 해야 하는지 제안
: 그래서 React에서는 상태를 직접 바꾸는 대신, setState 또는 set함수를 통해 새로운 참조를 가진 값으로 변경해야 합니다.
->
예시 또는 마무리 한줄
: 예를 들어 객체 안의 값을 바꿀 땐, spread 문법으로 새 객체를 만들어서 상태를 업데이트하는 방식으로 처리합니다.
A. React에서 상태는 불변성을 유지해야 하기 때문에 직접 상태를 수정하지 않고 setState 혹은 set함수를 사용합니다.
만약 상태를 직접 수정하게 되면 React는 변경을 감지하지 못해 리렌더링이 일어나지 않을 수 있습니다.
React는 상태가 바뀌었는지를 기존 참조와 새 참조를 비교(shallow compare)하여 판단합니다. 그런데 직접 객체를 수정하면 참조가 바뀌지 않기 때문에, React는 "아 상태 안 바뀌었네~" 하고 인식해서 리렌더링이 안 됩니다.
이로 인해 UI가 업데이트되지 않거나, 버그가 발생할 수 있어요.
실제로 기술면접에서 답변을 하게 될떄 실무에서 이 코드가 어떻게 적용이 되고 어떤식으로 풀어나가야하는지 조금은 감을 잡게 된것같다.. 기술면접에서도 저 써봤습니다.를 언급하기 위해서는 어제의 주제처럼 많은 프로젝트에서 Promise.all이라던지 Promise.race와 같은 방법들도 많이 응용해보고 다루어봐야 될 것같다. 그런점에서 cors와 같은 질문이 나왔을 떄, 실제로 내가 프로젝트에서 겪고 해결한 방안이 있었고 바로 대답할 수 있었기에 유용하였던것 같다. 최대한 많은 코드를 접해보고 많은 개념들을 알아봐야지.