English

프론트엔드 수업을 듣게 된 이유

나는 인간-AI 상호작용(HCI) 분야를 연구하고 있는 문과 출신 대학원생이다. 전공자가 아니었기에, 개발에 관해 배운 건 학교 수업이 아니라 유튜브 강의, 블로그, AI와의 대화를 통해서였다. 대학원에 들어온 이후에는 연구를 하며 그때그때 필요한 것을 익히고 있다. 그렇게 독학하다 보니 뭔가 만들 수는 있는데, 내가 짠 코드가 잘 짠 코드인지, 표준적인 방식을 따르고 있는지 알 수 없었고, AI를 사용하며 이러한 의문은 심화되었다.

이에 학부생 권장 프론트엔드 수업을 듣기로 결심했다. HTML/CSS에서 시작해 JavaScript, 그리고 React까지 직접 짜보면서 프론트엔드 개발의 전체적인 구조를 이해할 수 있었고, 패러다임이 어떤 흐름으로 바뀌어왔는지를 몸으로 이해하고 싶었다.

수업 과정

수업 과제에서 가장 인상 깊었던 경험은 같은 서비스를 다른 방식으로 개발해보는 경험이었다. To-do 앱을 다른 문법으로 작성해보기도 하고, DB를 붙이며 프론트와 백엔드를 분리하기도 하였다. 

화면을 그리는 방법

첫 주는 JavaScript만으로 Todo 앱을 만들었다. 데이터는 배열 하나, UI는 DOM 조작으로 직접 그렸다.

function renderTodos() {
todoList.innerHTML = '';
todos.forEach(todo => {
const li = createTodoElement(todo);
todoList.appendChild(li);
});
}


할 일을 추가하면 배열에 넣고, 그다음 renderTodos()를 호출해 화면을 통째로 다시 그리도록 설계했다. 이 때, 데이터를 바꾸면 개발자가 직접 "이제 화면을 다시 그려라"고 명령해야 한다. 이 방식은 직관적이다. 하지만 한 가지 질문이 생겼다. 화면에 요소가 100개라면? 어떤 이벤트마다 어떤 요소를 다시 그려야 하는지 일일이 추적해야 한다. 앱이 커질수록 개발자가 챙겨야 할 것이 기하급수로 늘어난다. 일을 추가하면 배열에 넣고, 그다음 renderTodos()를 호출해 화면을 통째로 다시 그리도록 설계했다. 데이터를 바꿀 때마다 개발자가 직접 화면을 다시 그리라는 명령을 내려야 하는 방식이다. 직관적이긴 하지만, 앱의 규모가 커질수록 한계가 드러난다. 이벤트마다 어떤 요소를 갱신해야 하는지 일일이 추적해야 하고, 개발자가 챙겨야 할 것이 기하급수적으로 늘어난다.

데이터 저장

두 번째 Todo 앱에서는 Express 서버와 MongoDB를 연결했다. 이제 데이터는 브라우저 메모리가 아니라 데이터베이스에 저장된다. 새로고침해도 사라지지 않는다. 클라이언트는 더 이상 데이터를 직접 들고 있지 않고, 필요할 때 서버에 요청해서 받아온다.

export async function fetchTodos() {
    const res = await fetch(`${BASE_URL}/todos`);
    return res.json();
}

export async function createTodo(title) {
    const res = await fetch(`${BASE_URL}/todos`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ title, completed: false }),
    });
    return res.json();
}

컴포넌트 개념

React로 넘어오면서 에어비앤비 클론 코딩을 하게 되었다. 이 과정에서 UI를 독립적인 단위로 바라보는 컴포넌트 개념을 배웠다. 에어비앤비는 헤더, 검색창, 카드 목록처럼 역할이 구분되는 영역들로 이루어져 있었는데, React에서는 이 각각을 독립적인 컴포넌트로 분리한다.

<BrowserRouter>
    <Header onSearch={handleSearch} />
    <Routes>
        <Route path="/" element={<MainPage results={results} hasSearched={hasSearched} />} />
    </Routes>
</BrowserRouter>

HTML 태그처럼 쓸 수 있고, 필요한 데이터는 props로 전달한다. 각 컴포넌트는 자신에게 전달된 데이터에만 반응하기 때문에, 어떤 데이터가 바뀌었을 때 화면의 어느 부분이 바뀌는지 추적하기가 훨씬 쉬워진다.

상태 관리 방식도 달라졌다. 검색 결과를 useState로 선언해두면, setResults(data)를 호출하는 것만으로 관련 컴포넌트가 자동으로 다시 렌더링된다.

const [results, setResults] = useState([]);

const handleSearch = async ({ city, guests }) => {
    const data = await fetch(`/api/accommodates?${params}`).then(r => r.json());
    setResults(data);
};

 

AI와 기초 사이에서

수업 과제를 통해 기초를 다지는 과정에서 직접 불편한 문법을 겪어본 게 가장 기억에 남는다. 이전까지는 AI가 추천해준 최선의 프레임워크를 갖다 쓰기만 했었는데, 수동으로 DOM을 조작하는 것의 번거로움을 해결하기 위한 방안으로 React의 상태 관리가 존재하게 되었다는 것을 알게 되었다. 

AI는 훌륭한 도구다. 막히는 부분을 빠르게 풀어주고, 내가 생각지 못한 방향을 제안해주기도 한다. 하지만 AI가 내놓는 코드를 이해하려면 결국 기초 개념이 필요하다. 이 수업을 통해 앞으로 코드를 짤 때도 AI에게 코드를 물어보기 전에 먼저 '이 문제를 왜 이렇게 풀어야 하는가'를 스스로 설명해 보는 습관을 기르고자 한다. AI는 내가 이해한 것을 확장해주는 도구여야지, 이해를 대신해주는 도구가 되면 안 된다는 것을 의식하며 앞으로의 개발 지식을 학습하고 싶다.

← Back to blog