JOBKOREA X ALBAMON

웹퍼블리셔의 새로운 길: 프론트엔드 개발 도전 이야기

JOBKO_유선영
jobkorea-tech
Published in
13 min readAug 14, 2024

--

안녕하세요, 잡코리아 FE플랫폼팀의 유선영, 박소혜, 배상훈입니다.

저희는 원래 UI개발파트에서 웹퍼블리셔로 일하고 있었어요. 프론트엔드 개발자로 전환하려고 계속 공부하다가 좋은 기회를 만나 이번 KLiK(클릭) 프로젝트에 프론트엔드 개발자로 참여하게 됐습니다. 저희는 이 프로젝트를 통해 정말 많이 배우고 성장할 수 있었습니다.

이 글에서는 저희가 프론트엔드 개발자로서 첫 프로젝트를 진행하면서 도움 받았던 일들과 어려웠던 점 등을 이야기해보려고 합니다. 이걸 통해 다른 웹퍼블리셔 분들이 개발자로 전환하는 데 도움이 되었으면 좋겠어요.

KLiK이 무엇인지 궁금하신 분이 있을 것 같아서 간단하게 소개해 드릴게요.

KLiK은 우리나라에 살고 있는 외국인들이 손쉽게 공고를 확인하고 지원할 수 있도록 도움을 주기 위해 만들어진 외국인 대상의 구인구직 서비스예요. KLiK은 ‘Kickstart Life In Korea’의 약자로, 잡코리아가 외국인 구직자의 안내자로서 한국에서의 첫걸음을 함께하겠다는 의미를 담고 있어요.

저희에게는 KLiK이 프론트엔드 개발자로서 첫걸음을 시작하게 해준 고맙고 소중한 존재이지요.

클릭 [https://klik.co.kr]

저희는 웹퍼블리셔로 근무하며 프론트엔드 개발의 실무를 경험했으며, 이 과정에서 기본적인 개발 역량을 쌓아 왔다는 공통점이 있습니다.

그러던 중, 잡코리아에서 야심차게 준비하는 KLiK 프로젝트(이하 ‘K’)에 새내기 개발자로서 막대한 부담감과 기대감을 가지고 참여를 하게 되었습니다.

‘K’에서 사용되는 주요 기술 스택은 React, Next.js, TypeScript 입니다.

이 외에도, ‘K’는 zod, zustand, react-hook-form, tanstack-query, vanilla-extract 등의 라이브러리를 기반으로 구축되어 있어요. 이러한 도구들은 프로젝트의 복잡성을 관리하고 효율적인 개발을 지원하는 중요한 역할을해요.

그럼, 이제 저희의 이야기를 시작해 볼게요! 🎬

‘K’의 아침 ☀️

저희 하루 일과는 기본적으로 데일리스크럼으로 시작합니다. 새내기 개발자인 만큼, 매일 짧게 진행되는 스크럼이 전반적인 개발 프로세스와 동료들의 진행 상황을 파악 하는 데 큰 도움이 되었어요. 스크럼 초반에는 모두가 다소 어리둥절해 했던 기억이 지금도 부끄럽게 느껴지네요.

초반에는 업무를 체계적으로 정리하는 것 조차 어렵고, 하루에 처리 할 수 있는 업무량을 파악하는 데도 어려움이 많았습니다. 그래서 스크럼 시간이 다소 부담스럽기도 했지만 한편으로 가장 도움이 되었던 시간이라는 생각이 들어요. 하루의 업무를 정리하고 계획하는 데 있어 꼭 필요한 시간이였거든요.

매일 아침 작성한 스크럼 문서 👍🏻 계획대로 진행중, 👎🏻 지연됨, ✅ 완료, ❌ 진행안함 으로 업무 상태를 바로 체크!

그리고 저희 데일리스크럼의 특징이라고 한다면 자유로운 질문이 가능하다는 점이었어요 🙋‍♂️🙋‍♀️️️️️️.️ 원래는 이 시간에 코드에 관련된 질문은 자제하는 것이 원칙이지만 모르는 게 많았던 저희에게는 질문을 편하게 할 수 있는 가장 좋은 시간이였어요. 지금 돌이켜보면 철 없는 개발자였지만 다들 귀찮아하지 않고 성실히 답변해주셔서 정말 많은 도움이 됐습니다.

‘K’의 개발일기 📝

‘K’를 하면서 어려웠던 점을 묻는다면 사실 처음부터 끝까지 라고 볼 수 있죠. 작은 산을 넘어가면 더 큰 산이 기다리고 있었거든요 🤣

각자 맡은 파트의 PoC(Proof of Concept)부터 컴포넌트 개발까지 매일 새로운 문제와 직면하고, 이를 해결하는 과정의 연속이었어요.

‘K’에서의 첫 업무는 공통 컴포넌트 개발! 하지만 그 과정은 시작부터 쉽지 않았어요. 이를 극복하기 위해 우리는 서로의 개발 코드를 공유하면서 매일 오전 30분씩 짧은 스터디 시간을 가지기도 했답니다 📚.

그러나 실제 페이지 작업에 들어가면서 예상치 못한 문제들이 생겨났어요.

그중에서도 메인 검색 필터에 대한 개발 진행 중, 필터 로직에 대해 많은 고민을 했음에도 작업하다 보니 미처 생각지 못한 부분들이 발생했어요.

1. 새로고침 해도 필터 상태가 유지되려면 어떤 방법으로 해야하는지,

2. 그 방법이 성능 상 문제가 없는지,

이 밖에도 세세하게 고려되지 못한 부분이 있어, 불필요한 루프로 콘솔로그가 몇백 개가 찍히는 상황을 마주하였어요. ☠️

상태 초기화 과정에서 필터항목의 모든 배열을 탐색하여 문제가 되었는데요. 혼자서는 도저히 해결이 안되서 리더님께 도움을 요청했습니다.

짜잔! 리더님의 손을 거친 코드 퀄리티 👍🏻

useSearchParams 을 이용해 얻은 쿼리 스트링으로 필터 상태를 초기화 하는 방법으로 해결해주셨는데, 왜 이 생각을 못했을까?

기초와 경험의 중요함을 느꼈어요. 😥

처음엔 작은 단위의 컴포넌트로 페이지를 만드는게 익숙하지 않아 한 페이지에 모든 코드를 담아 정신 없는 상태가 되었던 것 같아요. 그러나 동료들의 코드 리뷰 덕분에 비즈니스 로직과 컴포넌트를 효과적으로 사용하는 방법에 대해 고민하며 작업을 진행할 수 있었습니다.

“저 코드 리뷰 해주세요!!🙋🏻‍♀️” 메인 필터 작업 굴레에서 한줄기 빛 ⭐️ 코드 리뷰

메인에서는 검색 필터로 인해 Modal이 여러개가 필요했어요. 모달을 useState로 각각 상태관리를 해주었는데, 코드 리뷰를 통해 Custom Hook으로 여러 Modal의 On / Off 상태를 관리하는 것으로 수정 하기도 했습니다.

// before
const [isLocationModalOpen, setIsLocationModalOpen] = useState(false);
const [isJobClassificationModalOpen, setIsJobClassificationModalOpen] = useState(false);
const [isLanguageModalOpen, setIsLanguageModalOpen] = useState(false);
const [isVisaModalOpen, setIsVisaModalOpen] = useState(false);
// after
const { closeModal, modalOpenId, openModal } = useModalManager({
idList: ['JobClassificationCheckModal', 'LocationCheckModal', 'LanguageCheckModal', 'VisaCheckModal'],
useMultiOpen: false,
});

코드 리뷰 받은 대로 수정하니 코드가 훨씬 간결해졌죠!!👍

이처럼 I인 상훈님도, E인 소혜님도, 부끄럼 많은 선영님까지, 모두가 코드를 완성한 후 코드 리뷰를 요청하면 ‘짜잔!, AI 보다 더 뛰어나신 선배개발자님들이 코드리뷰를 해주신다!! 🚀 ’ 는 점이, 저희같은 새내기 개발자에게는 큰 정신적 지지가 되었습니다.

하지만 막강한 코드리뷰에도 난관은 있는 법 🧗.

저희가 ‘K’에서 공통적으로 어려움을 느꼈던 부분은 바로 “ReactQuery”였습니다.

아무래도 저희는 웹페이지 단위의 UI 구현에는 큰 두려움이 없었어요. 웹브라우저에서 HTML과 CSS를 통하여 인터페이스를 구현하는것이 웹퍼블리셔의 주 업무 였으니까요.

그러나 ‘K’에서는 단순한 웹페이지를 만드는 것이 아니라, 웹 애플리케이션의 복잡한 동적 기능을 구현해야 했습니다. 이 과정에서는 서버와 비동기 통신을 통해 데이터를 연동하고, 서비스의 작동 방식을 깊이 이해해야 했죠.

즉, 단순히 정적인 UI를 넘어서서, 서비스의 전체적인 구조를 파악하고, 동적인 인터페이스를 구현해야 하는 도전이 있었습니다. 특히 백엔드 개발자가 만든 API를 사용하기 위해서는 먼저 Swagger라는 다소 생소한 툴의 사용방법을 이해하고 활용해야 했었어요.

💡 Swagger는 웹 서비스의 API를 가독성 높은 웹문서로 만들어주는 오픈 소스 프레임워크입니다. 이 도구를 통해 웹 서비스의 로직과 필요한 값, 응답 내용 등을 쉽게 파악할 수 있으며, API가 업데이트되면 문서도 자동으로 갱신됩니다.

개발을 진행하다 보면 가끔 업데이트가 안된 문서를 찾거나, 백엔드 개발자에게 API 스펙을 문의하느라 시간이 낭비되는 경우가 생기곤 하는데 Swagger는 그러한 시간을 획기적으로 단축시켜 주는 고마운 툴이였어요 👍

SwaggerUI에서 GET, PUT, POST, DELETE 등의 Method와 Parameter를 확인하고,

개발중인 서비스로 돌아와 API 규칙에 맞게 서버와 통신하는 코드를 추가합니다.

const profilesPortfoliosApiMethod = {
post: ({
headers,
params,
requestBody: { portfolioUrl },
}: PortfoliosApiRequestParams<V1IndividualProfilesIdPortfoliosPostParameters>) => {
return apiFetch.post<V1IndividualProfilesIdPortfoliosPostResponse>(
getEnv('DISPLAY_URL'),
getPostApiPath(params),
{ data: { portfolioUrl } },
headers,
);
},
};

이 코드는 서버와 통신하는 기본적인 작업만 수행하며, 데이터 캐싱이나 리페칭과 같은 추가 기능은 포함하지 않았습니다.

따라서 실제 클라이언트에서 데이터 캐싱과 리페칭이 가능하도록 QueryKey 모듈을 연동하고, 클라이언트 코드에 맞는 Mutation 코드를 작성했습니다.

export const usePostProfilesPortfoliosMutation = (params: V1IndividualProfilesIdPortfoliosPostParameters) => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (requestBody: V1IndividualProfilesIdPortfoliosIdPutRequest) =>
profilesPortfoliosApiMethod.post({ requestBody, params }),

async onSuccess() {
await queryClient.invalidateQueries({ queryKey: [QUERY_KEY.PROFILES_HOME] });
},
});
};

이렇게 API의 코드를 분할 한 덕분에 client에서는 복잡한 API 기능을 단 한줄로 손쉽게 사용 할 수 있었답니다.

const { mutateAsync: postPortfolio } = usePostProfilesPortfoliosMutation({
profileId: session?.profileId || '',
});

이렇게 ReactQuery의 역할과 기능을 실전에서 하나씩 이해하고, API 통신을 구현해 나가면서 자연스럽게 프론트엔드 개발자로서의 사고방식으로 전환되는 것을 느꼈습니다.

“분명 UI는 자신있었는데..?”

웹퍼블리셔로 UI개발을 할 때는 HTML과 SCSS를 사용한 스타일링 작업이 익숙하고 자신 있었어요. 하지만 개발자가 되어 vanilla-extract 같은 CSS-in-JS 도구를 접하면서 새로운 도전과 어려움을 겪게 되었죠. CSS-in-JS는 기존의 방식과는 달리 자바스크립트와 CSS를 결합하여 스타일을 정의하는 방법으로, 초기 설정과 구조화 과정에서 많은 시간과 노력이 필요했어요.

SCSS에서는 클래스명을 정의하고, 네스팅을 통해 스타일을 관리하는 것이 일반적이었습니다.

/* 웹퍼블리셔 시절 SCSS 코드 예시 */
.button {
background-color: #007bff;
padding: 10px;
&--text {
width: 100%;
color: #fff;
}
&:hover {
background-color: #0056b3;
}
}

그러나 vanilla-extract를 사용하게 되면서 완전히 다른 방식으로 스타일을 정의해야 했습니다. 이제 스타일을 JavaScript 코드안에서 작성해야 했죠.

// vanilla-extract 코드 예제 
import { style } from ' @vanilla-extract/css' ;

export const button = style ({
backgroundColor : colors.secondary.pink , padding
: 10 , '
:hover' : {
backgroundColor : colors.violetGray , }
,
});

export const buttonText = style ( {
width : '100%' ;
selectors : {
[ ` $ {button} &` ]: {
color : colors.primary.violet600 , } , } } );

이 방식은 웹퍼블리셔 시절의 SCSS와는 많이 다르기 때문에 처음에는 혼란스럽기도 했어요 🤦‍♀️

vanilla-extract에서는 스타일을 JavaScript 객체 형태로 작성하고, 이를 컴포넌트와 함께 관리하게 되는데요, 필요한 것만 import 해서 쓸 수도 있어 관리만 잘 한다면 훨씬 스타일 작업에 효율을 높일 수 있을 거라는 생각이 들었어요. 그리고 타입스크립트를 통해 CSS 속성을 바로 찾을 수 있어, 오타로 인한 실수도 방지할 수 있었답니다.

웹퍼블리셔로서 익숙했던 스타일링 작업이 개발자의 입장에서 어떻게 변화하는지 체감할 수 있었고, 더 나아가 JavaScript와의 연관성 속에서 스타일링을 이해하고 적용하는 능력을 기를 수 있었습니다.

“함께라서 가능했던 개발 도전기” 🧑‍💻

‘K’를 진행하는 동안 가장 큰 도움이 된 것은 역시 동료들의 코드 리뷰와 클론코딩이였죠. 앞에서 라이브 코딩을 하며 설명해주셨던 모습은 지금도 인상 깊게 남아있어요. 초기에 익숙하지 않았던 저희를 위해 점심시간까지 반납하며 개발에 유용한 확장 프로그램 추천부터 클론 코딩, 코드 리뷰까지 친절하게 도와주셨던 동료분들 덕분에 프론트엔드 개발자로써 걸음마(?)를 익히게 되었죠.

이러한 도움 덕분에 끝까지 달릴수 있었습니다🏃‍♂️️️🏃

IN CONCLUSION.

짧지 않은 기간 동안 첫 개발자로서 프로젝트를 무사히 완성하여, 외국인 특화 구인구직 서비스 KLiK이 2024년 7월 4일 드디어 오픈했습니다! 🎉🎉🎉🎉🎉

오픈하는 날의 기분을 결코 잊지 못할 것 같아요. 이전의 퍼블리싱 작업과는 달리, 오픈 전날에는 잠도 제대로 오지 않더라고요🥹. 서비스가 인터넷에 검색 가능해지고 실제 가입자들이 생기기 시작했을 때의 성취감은 말로 표현할 수 없을 정도였어요. 그 순간, 지금까지의 모든 고생과 어려움이 모두 사라지는듯한 기분이었어요.

우여곡절도 많고 힘든 순간도 많았지만, 좋은 분들과 함께하며 다양한 경험을 쌓고 즐겁게 몰입해서 일할 수 있었던 시간에 모두에게 감사하다는 말씀을 전하고 싶습니다💕. 앞으로도 프론트엔드 개발자로서 성장해 나가며, 언젠가 저희도 새로운 새내기 개발자들을 이끌어 나갈 수 있는 날이 오기를 바라며!

‘K’! 마무리하겠습니다! 👋

--

--