Skip to main content

37장. 화면과 서버를 연결하는 흐름 이해하기

[7부] 화면과 서버를 연결하는 흐름 이해하기

[7부] 화면과 서버를 연결하는 흐름 이해하기

[7부] 화면과 서버를 연결하는 흐름 이해하기

버튼 하나를 누르면, 생각보다 많은 일이 일어난다

지금까지 우리는 화면도 만들었고, 서버도 만들었고, 회원가입과 로그인, 글 작성과 수정, 삭제까지 각각의 기능도 만들어 보았다. 이제 중요한 단계가 남았다. 따로따로 만든 것들이 실제로 어떻게 이어지는지 머릿속에서 한 장의 그림으로 정리하는 일이다.

초보자가 가장 자주 막히는 순간은 바로 여기다. 화면은 멀쩡해 보이는데 데이터가 안 뜨고, 서버 코드는 있어 보이는데 버튼을 눌러도 아무 일도 일어나지 않는다. 이럴 때 필요한 것은 더 어려운 문법이 아니라, "지금 요청이 어디쯤 가고 있지?"를 떠올릴 수 있는 감각이다.

화면과 서버의 연결은 결국 요청과 응답의 흐름이다. 사용자가 화면에서 어떤 행동을 하면 프론트엔드가 서버에 요청을 보내고, 서버는 그 요청을 처리한 뒤 응답을 돌려준다. 프론트엔드는 그 응답을 받아 다시 화면을 바꾼다. 이 한 줄을 제대로 이해하면, 복잡해 보이던 웹 서비스가 훨씬 단순하게 보이기 시작한다.

전체 흐름을 먼저 한 문장으로 잡아보자

웹 서비스에서 데이터가 오가는 기본 흐름은 대체로 이렇다.

  • 사용자가 화면에서 행동한다.

  • 프론트엔드가 요청을 만든다.

  • 서버가 요청을 검사하고 처리한다.

  • 필요하면 데이터베이스와 통신한다.

  • 서버가 결과를 응답으로 돌려준다.

  • 프론트엔드가 화면을 다시 그린다.

이 흐름은 게시글 목록을 불러올 때도 같고, 글을 저장할 때도 같고, 로그인할 때도 같다. 기능은 달라도 뼈대는 거의 같다. 그래서 이 구조를 몸에 익히는 것이 중요하다.

쉽게 비유하면 프론트엔드는 주문을 받는 홀 직원이고, 서버는 주방이다. 데이터베이스는 재료 창고에 가깝다. 손님이 메뉴를 말하면 홀 직원이 주문을 정리해서 주방에 전달한다. 주방은 재료 창고를 확인하고 음식을 만든 뒤 다시 홀로 보낸다. 홀 직원은 그 결과를 손님에게 가져다준다. 음식이 늦게 나오면 어디를 봐야 할까? 주문이 전달되지 않았는지, 주방이 바쁜지, 재료가 없는지 차례대로 확인하면 된다. 웹 서비스도 똑같다.

프론트엔드는 시작점이다

요청은 대개 화면에서 시작된다. 사용자가 페이지에 처음 들어오거나, 버튼을 누르거나, 폼을 제출하거나, 검색어를 입력할 때다.

예를 들어 사용자가 "글 목록" 화면에 들어왔다고 해보자. 화면은 그냥 빈 종이가 아니라, 서버에서 글 데이터를 받아와 채워 넣어야 하는 공간이다. 이때 프론트엔드는 "지금 글 목록 데이터가 필요하다"라고 판단하고 서버에 요청을 보낸다.

반대로 사용자가 "글 작성" 버튼을 누르고 제목과 내용을 입력한 뒤 저장을 눌렀다면, 프론트엔드는 입력값을 모아 서버로 보낸다. 여기서 프론트엔드는 단순히 예쁘게 보여주는 역할만 하는 것이 아니다. 사용자의 행동을 읽고, 필요한 데이터를 묶어서, 올바른 목적지로 보내는 출발점 역할도 한다.

이 단계에서 프론트엔드가 하는 일은 생각보다 분명하다. 입력값을 읽고, 필요한 최소한의 검사를 하고, 어떤 주소로 어떤 방식으로 요청할지 결정한다. 그리고 요청이 진행 중인지, 성공했는지, 실패했는지를 기억해 화면에 반영한다.

요청에는 항상 몇 가지 요소가 들어 있다

서버에 "그냥 좀 해줘"라고 보낼 수는 없다. 요청에는 최소한의 형식이 필요하다. 초보자 입장에서는 다음 네 가지를 기억하면 충분하다.

첫째, 어디로 보낼 것인지가 필요하다. 이것이 요청 주소다. 예를 들어 글 목록을 가져오는 주소와 글을 저장하는 주소는 다르다.

둘째, 무엇을 하려는지 방식이 필요하다. 데이터를 가져올 때와 새로 만들 때는 같은 요청이 아니다. 보통 조회는 GET, 생성은 POST, 수정은 PATCH나 PUT, 삭제는 DELETE처럼 구분한다. 처음에는 용어가 낯설 수 있지만, "읽기/만들기/고치기/지우기" 버튼에 이름표가 붙어 있다고 생각하면 쉽다.

셋째, 무엇을 보낼 것인지 내용이 필요하다. 글 작성이라면 제목과 내용이 들어갈 것이고, 로그인이라면 이메일과 비밀번호가 들어간다. 이런 실제 데이터 묶음을 보통 본문, 혹은 payload라고 부른다.

넷째, 누구인지에 대한 정보가 필요할 수 있다. 로그인한 사용자만 가능한 기능이라면 서버는 요청을 보낸 사람이 누구인지 알아야 한다. 이 정보는 쿠키나 토큰 같은 방식으로 함께 전달된다.

이 네 가지가 맞지 않으면 화면은 멀쩡해 보여도 서버는 제대로 일할 수 없다.

서버는 요청을 받으면 바로 데이터베이스부터 가지 않는다

초보자에게 서버는 종종 "데이터베이스랑 바로 연결된 무언가"처럼 보인다. 하지만 실제로 서버는 중간에서 꽤 많은 판단을 한다.

서버는 요청을 받으면 먼저 이 요청이 유효한지 본다. 값이 빠지지 않았는지, 로그인한 사용자인지, 수정 권한이 있는지, 잘못된 형식은 아닌지 확인한다. 그다음에야 데이터베이스에 조회하거나 저장한다. 그리고 마지막으로 결과를 응답 형태로 정리해서 프론트엔드에 돌려준다.

즉, 서버는 단순한 전달자가 아니다. 규칙을 지키는 문지기이자, 필요한 작업을 조립하는 관리자에 가깝다.

예를 들어 사용자가 다른 사람의 글을 수정하려 한다면, 프론트엔드에서는 수정 버튼이 보일 수도 있다. 하지만 진짜로 막아야 하는 곳은 서버다. 서버는 요청을 보낸 사람의 사용자 정보와 글 작성자를 비교해서 권한이 없으면 거절해야 한다. 이 검사가 없으면 화면만 바꿔서는 보안을 지킬 수 없다.

데이터베이스는 기억을 담당한다

서버가 판단을 마쳤다면 이제 데이터베이스 차례다. 데이터베이스는 서비스의 기억 저장소다. 목록을 보여주려면 저장된 글을 꺼내와야 하고, 새 글을 작성하면 기록을 남겨야 하며, 삭제하면 지워야 한다.

이때 중요한 점은 프론트엔드가 보통 데이터베이스와 직접 대화하지 않는다는 것이다. 중간에 서버가 있다. 프론트엔드는 "이 글을 저장해 주세요"라고 서버에 말하고, 서버가 데이터베이스에 실제 저장 작업을 수행한다.

이 구조가 중요한 이유는 역할이 분리되기 때문이다. 화면은 사용 경험을 책임지고, 서버는 규칙과 처리 과정을 책임지고, 데이터베이스는 저장을 책임진다. 셋이 섞이면 기능은 돌아가도 나중에 고치기 어려워진다.

응답은 서버의 답장이다

서버가 일을 끝내면 프론트엔드에 결과를 돌려준다. 이것이 응답이다. 응답에는 보통 두 가지가 들어 있다. 하나는 성공했는지 실패했는지에 대한 상태이고, 다른 하나는 실제 결과 데이터다.

예를 들어 글 목록 요청이라면 서버는 여러 개의 글 데이터를 응답으로 보낸다. 글 작성 요청이라면 새로 저장된 글의 정보나 성공 메시지를 돌려줄 수 있다. 로그인이라면 사용자 정보나 로그인 상태가 응답에 담길 수 있다.

프론트엔드는 이 응답을 보고 다음 행동을 정한다. 성공했다면 목록을 새로 불러오거나 상세 페이지로 이동할 수 있다. 실패했다면 오류 문구를 보여줘야 한다. 응답을 받았는데도 화면이 그대로라면, 문제는 서버가 아니라 응답을 받은 뒤 화면을 바꾸는 프론트엔드 쪽일 가능성이 높다.

글 목록 조회로 흐름을 그려보자

가장 단순한 예로 "글 목록 보기"를 생각해 보자.

사용자가 홈 화면에 들어온다. 프론트엔드는 페이지가 열리자마자 서버에 글 목록을 요청한다. 서버는 요청을 받고, 필요한 조건이 있으면 확인한 뒤 데이터베이스에서 최신 글 목록을 조회한다. 조회 결과를 정리해서 응답으로 돌려준다. 프론트엔드는 그 데이터를 받아 카드나 리스트 형태로 화면에 출력한다.

이 과정을 머릿속에서 그림처럼 떠올리면 좋다.

사용자 입장에서는 "페이지에 글이 뜬다"로 끝나지만, 개발자 입장에서는 그 뒤에 네 단계가 숨어 있다. 요청, 처리, 조회, 렌더링이다.

만약 이때 글이 보이지 않는다면 어디를 봐야 할까? 요청이 아예 안 나갔는지, 서버가 빈 배열을 돌려줬는지, 데이터베이스에 실제 데이터가 없는지, 받아 온 데이터를 화면에서 제대로 돌려 그리고 있는지 하나씩 쪼개서 보면 된다. 막연히 "왜 안 되지?"라고 바라보는 것보다 훨씬 빨리 원인을 찾을 수 있다.

글 작성은 조회보다 한 단계 더 역동적이다

이제 "글 작성"을 보자. 사용자가 제목과 내용을 입력하고 저장 버튼을 누른다. 프론트엔드는 폼 값을 읽어 서버에 전송한다. 서버는 사용자가 로그인했는지 확인하고, 제목과 내용이 비어 있지 않은지 검사한다. 이상이 없으면 데이터베이스에 새 글을 저장한다. 저장이 성공하면 서버는 새 글 정보 또는 성공 응답을 돌려준다. 프론트엔드는 이 응답을 받아 상세 페이지로 이동하거나 목록을 다시 불러온다.

여기서 중요한 것은 저장 버튼을 눌렀다고 해서 바로 화면에 반영되는 것이 아니라는 점이다. 실제로는 "요청을 보냈고, 서버가 받아 저장했고, 저장 결과가 돌아왔기 때문에" 화면이 바뀐다.

이 차이를 이해하면 디버깅이 쉬워진다. 저장 버튼을 눌렀는데 아무 반응이 없으면 먼저 폼 제출 함수가 실행됐는지 본다. 실행됐다면 요청이 서버로 나갔는지 본다. 서버에 도착했다면 저장 쿼리가 성공했는지 본다. 저장은 됐는데 화면이 안 바뀌면 성공 이후 이동이나 목록 갱신 로직을 본다.

수정과 삭제에서는 "누구의 데이터인가"가 더 중요해진다

조회와 생성은 비교적 단순하지만, 수정과 삭제는 권한 문제가 더 자주 등장한다. 사용자가 수정 버튼을 눌렀다고 해서 누구의 글이든 바꿀 수 있으면 안 된다. 삭제도 마찬가지다.

이때 프론트엔드는 보통 현재 로그인한 사용자에게만 수정 버튼을 보여주도록 만들 수 있다. 하지만 그것만으로는 부족하다. 서버는 요청을 받았을 때 해당 글의 작성자와 현재 사용자 정보를 다시 비교해야 한다. 그리고 권한이 없으면 거절해야 한다.

즉, 프론트엔드의 역할은 사용성을 좋게 만드는 것이고, 서버의 역할은 규칙을 강제하는 것이다. 둘 다 필요하지만, 최종 판단은 서버가 맡는다.

연결 흐름을 이해하면 상태라는 말도 쉬워진다

프론트엔드 개발에서 자주 듣는 말이 상태다. 처음 들으면 거창해 보이지만, 사실은 화면이 기억하고 있는 값이다. 연결 흐름 안에서 상태는 특히 중요하다.

예를 들어 글 목록 화면에는 최소한 세 가지 상태가 있다. 아직 불러오는 중인지, 성공해서 데이터가 있는지, 실패했는지다. 어떤 경우에는 데이터는 성공적으로 받아왔지만 아무 글도 없는 상태도 따로 다뤄야 한다.

글 작성 화면도 마찬가지다. 입력값 상태가 있고, 저장 중인지 아닌지가 있고, 저장 성공 후 이동할지, 실패 문구를 보여줄지가 있다. 즉, 프론트엔드는 서버와 통신한 결과를 상태로 기억하고, 그 상태에 맞춰 화면을 바꾼다.

그래서 화면과 서버 연결은 단순히 fetch 한 줄 쓰는 문제가 아니다. 요청 전, 요청 중, 요청 후에 화면이 어떤 표정을 지을지까지 포함한 이야기다. 다음 장에서 로딩, 실패, 빈 화면을 따로 다루는 이유도 바로 여기에 있다.

초보자가 자주 헷갈리는 연결 지점들

처음 풀스택 흐름을 다룰 때 많은 사람이 비슷한 지점에서 멈춘다. 대표적으로 이런 경우다.

  • 화면에서 버튼은 눌리는데 요청 함수가 실행되지 않는 경우

  • 요청은 나가는데 주소나 방식이 잘못된 경우

  • 서버까지 도착했지만 필수 값이 빠져 처리되지 않는 경우

  • 데이터베이스에는 저장됐는데 화면이 다시 갱신되지 않는 경우

  • 로그인은 되어 있는데 인증 정보가 요청에 함께 전달되지 않는 경우

  • 성공 응답을 받았는데 프론트엔드가 결과를 잘못 읽는 경우

이 문제들은 전부 비슷해 보이지만, 실제 원인은 각기 다른 층에 있다. 그래서 "어디서부터 어디까지는 정상인가?"를 확인하는 습관이 중요하다.

문제를 찾을 때는 층별로 끊어서 본다

에러가 나면 초보자는 보통 전체를 한 덩어리로 본다. 하지만 전체를 한 번에 보면 오히려 아무것도 안 보인다. 이럴 때는 층별로 끊어서 보면 된다.

먼저 프론트엔드 층을 본다. 클릭 이벤트가 실행되는지, 입력값이 제대로 읽히는지, 요청 함수가 호출되는지 확인한다.

다음으로 네트워크 층을 본다. 요청 주소가 맞는지, 요청 방식이 맞는지, 상태 코드가 무엇인지 확인한다. 상태 코드는 서버가 어떤 표정으로 답했는지를 보여주는 신호라고 생각하면 좋다. 200번대는 대체로 성공, 400번대는 요청 문제, 500번대는 서버 내부 문제에 가깝다.

그다음은 서버 층이다. 서버가 요청을 받았는지, 예상한 값이 들어왔는지, 권한 검사나 유효성 검사가 통과했는지 본다.

마지막으로 데이터베이스 층이다. 실제로 데이터가 저장되었는지, 조회 조건이 맞는지, 수정 또는 삭제 대상이 정확한지 확인한다.

이렇게 쪼개면 "전체가 안 된다"라는 막막함이 "프론트엔드는 정상, 서버 응답이 이상함"처럼 훨씬 다룰 수 있는 문장으로 바뀐다.

한 번에 이해되지 않으면, 한 요청만 따라가면 된다

처음부터 모든 기능의 연결 구조를 완벽하게 이해하려고 하면 머리가 복잡해진다. 그럴 때는 기능 하나만 골라 끝까지 따라가 보자. 가장 좋은 연습은 게시글 하나를 기준으로 흐름을 추적하는 것이다.

예를 들어 "게시글 저장" 하나만 붙잡고 다음 질문을 해보면 된다.

  • 저장 버튼을 누르면 어떤 함수가 실행되는가?

  • 그 함수는 어떤 데이터를 모아서 어디로 보내는가?

  • 서버는 어떤 규칙으로 그 요청을 검사하는가?

  • 데이터베이스에는 어떤 값이 저장되는가?

  • 서버는 어떤 응답을 돌려주는가?

  • 프론트엔드는 그 응답을 받고 무엇을 하는가?

이 여섯 질문에 답할 수 있으면, 연결 흐름을 꽤 잘 이해한 것이다. 그리고 이 감각은 로그인, 검색, 수정, 삭제, 파일 업로드 같은 다른 기능으로 거의 그대로 확장된다.

AI에게 물어볼 때도 흐름 단위로 질문해야 한다

이 장의 내용은 AI와 협업할 때도 매우 중요하다. 초보자는 종종 "안 돼요"라고만 말한다. 하지만 그 말만으로는 AI도 정확한 원인을 찾기 어렵다.

대신 흐름 단위로 말해야 한다. 예를 들어 "프론트엔드에서 submit 함수는 실행되고 콘솔에도 입력값이 찍히는데, 서버 응답이 401로 돌아옵니다"라고 말하면 문제 범위가 확 줄어든다. 또는 "데이터베이스에는 저장되는데 저장 후 목록 화면이 갱신되지 않습니다"라고 말하면, 서버 저장이 아니라 프론트엔드 갱신 로직을 봐야 한다는 힌트를 줄 수 있다.

좋은 질문은 문제를 작은 구간으로 자른 질문이다. 연결 흐름을 아는 사람은 AI에게도 훨씬 정확하게 도움을 받을 수 있다.

결국 중요한 것은 "지금 어디쯤인가"를 아는 감각이다

풀스택 개발이 어렵게 느껴지는 이유는 배워야 할 도구가 많아서이기도 하지만, 사실은 데이터가 보이지 않게 이동하기 때문이다. 눈앞에는 버튼 하나만 보이는데, 그 뒤에서는 프론트엔드, 서버, 인증, 데이터베이스가 차례로 움직인다. 이 흐름이 보이지 않으면 막막하고, 보이기 시작하면 갑자기 길이 열린다.

앞으로 기능이 더 복잡해져도 기본 원리는 같다. 사용자가 행동한다. 프론트엔드가 요청한다. 서버가 검사하고 처리한다. 데이터베이스가 저장하거나 꺼낸다. 서버가 응답한다. 프론트엔드가 화면을 바꾼다.

이 리듬만 놓치지 않으면, 에러가 나도 겁먹을 필요가 없다. 어디선가 연결이 끊긴 것뿐이다. 그리고 이제 우리는 그 연결이 어디에서 시작되고 어디에서 끝나는지 알고 있다.

이 장의 핵심 정리

화면과 서버의 연결은 결국 요청과 응답의 흐름이다. 프론트엔드는 사용자의 행동을 받아 요청을 만들고, 서버는 그 요청을 검사하고 처리한 뒤 응답을 돌려준다. 데이터베이스는 그중 저장과 조회를 맡는다.

기능이 안 될 때는 전체를 한 덩어리로 보지 말고, 프론트엔드, 네트워크, 서버, 데이터베이스로 끊어서 확인해야 한다. 그러면 막연한 불안이 구체적인 점검 순서로 바뀐다.

그리고 이 흐름을 이해하는 순간, 여러분은 더 이상 "코드를 잘 모르는 사람"이 아니다. 적어도 웹 서비스가 어떻게 움직이는지 아는 사람이다. 그 차이가 실제 서비스를 끝까지 완성하게 만든다.

책 목록

바이브 코딩으로 완성하는 첫 풀스택 웹 서비스

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기