서비스 학습
FE · RESPONSIBILITY

Frontend는 모델을 실행하지 않는다

React는 사용자 입력과 대화 화면을 관리한다. TypeScript와 Vite가 browser용 JavaScript·CSS로 변환하고, production에서는 Nginx가 완성된 정적 파일을 전달한다.

Why frontend exists

Frontend는 추론을 “사람이 사용할 수 있는 상호작용”으로 번역한다

TurboFieldfare의 API는 JSON 요청을 받고 JSON 또는 stream을 돌려준다. 컴퓨터에는 충분하지만 사람에게는 입력 상자, 전송 버튼, 대화 순서, 생성 중 표시, 오류 안내가 필요하다. Frontend는 모델 자체의 지능을 높이지 않지만 사용자가 모델의 상태를 이해하고 안전하게 조작하게 만든다.

책임의 경계: React는 “무엇을 화면에 보여 줄지”를 결정한다. 모델 파일을 읽거나 Metal kernel을 실행하지 않으며, API key·접근 권한처럼 숨겨야 할 정책의 최종 판단도 맡지 않는다.

실제 파일과 학습 문서의 대응

실제 source 역할 설명 문서
fe/Dockerfile 개발·build·production image 정의 Dockerfile.html
fe/.dockerignore build context에서 제외할 파일 Dockerfile의 context 절
fe/nginx.conf 정적 파일·SPA·health 제공 nginx.conf.html
fe/src/main.tsx React를 div#root에 연결하는 진입점 main.tsx.html
fe/src/App.tsx 채팅 화면과 state App.tsx.html
fe/vite.config.ts TSX를 브라우저용 파일로 바꾸는 설정 vite.config.ts.html
fe/index.html 브라우저가 처음 받는 문서 · div#root index.html.html
fe/src/index.css 전역 토큰과 브라우저 기본값 정리 index.css.html
fe/src/App.css 채팅 화면 레이아웃과 반응형 App.css.html
fe/package.json 의존성과 npm 명령 package.json.html
fe/tsconfig*.json 브라우저 코드와 도구 코드의 type 검사 tsconfig.json.html
fe/dist/ build 결과물 (자동 생성) 생성 과정과 대표 산출물
Browser internals

TSX 파일은 브라우저가 그대로 실행할 수 없다

현재 npm run buildtsc -b로 type을 검사한 뒤 vite build로 배포 파일을 만든다. npm run dev로 Vite 개발 서버를 띄운다는 사실만으로 별도 TypeScript 검사가 모두 수행되는 것은 아니다.

App.tsx에는 HTML처럼 보이는 JSX와 TypeScript type이 들어 있다. 브라우저의 JavaScript engine은 TypeScript type을 해석하지 못하므로 build 단계가 필요하다.

Parse문법 분석

Vite와 TypeScript가 source를 구문 트리로 읽는다.

TransformJS 변환

Vite가 TSX를 브라우저용 JavaScript로 변환한다. type 검사는 별도 tsc 단계다.

Bundle의존성 묶기

React와 여러 module을 배포하기 좋은 asset으로 연결한다.

LoadDOM 생성

브라우저가 JS를 실행하고 실제 화면 요소를 만든다.

JSX, DOM, 렌더링은 각각 무엇인가?

JSX는 개발자가 화면 구조를 읽기 쉽게 적는 문법이다. DOM은 브라우저 메모리 안에 만들어진 실제 문서 구조다. 렌더링은 현재 React state를 바탕으로 필요한 DOM 변경을 계산하고, 브라우저가 그 결과를 화면의 pixel로 그리는 과정이다. JSX 자체가 화면은 아니다.

Source에서 browser까지

작성 TSX · CSS

사람이 읽는 source.

검사 TypeScript

type 오류를 찾는다.

Build Vite

browser asset 생성.

Serve Nginx

HTTP로 전달.

중요: production container에는 source 편집 도구가 아니라 build 결과만 필요하다. 이 차이가 multi-stage Dockerfile을 사용하는 이유다.
Current state

현재 App.tsx는 무엇을 실제로 하는가?

const [sidebarOpen, setSidebarOpen] = useState(false)
const [prompt, setPrompt] = useState('')
const [messages, setMessages] = useState<string[]>([])
const send = () => { const text = prompt.trim(); if (!text) return; setMessages((current) => [...current, text]); setPrompt('') }

현재 messages는 role·content 객체 배열이 아니라 string[]이다. send는 입력 공백을 정리하고 빈 문자열을 거절한 뒤, 사용자 문장을 배열에 추가하고 입력 상자를 비운다. 각 문장 밑에는 “FastAPI가 연결되면…”이라는 고정 안내 문구를 표시한다. 실제 HTTP 요청, assistant 응답 저장, streaming, isGenerating은 아직 없다. 한 줄씩 해설과 실제 값 추적은 App.tsx 상세 문서에 있다.

현재: prompt="안녕" → send() → messages=["안녕"], prompt="" → 사용자 문장 + 고정 안내 문구를 화면에 표시 → 새로운 채팅 버튼: messages=[], prompt="", sidebarOpen=false

최근 대화 목록·모델 선택·검색·보관 버튼도 현재는 예시 UI이며 API와 연결된 기능이 아니다. 이 단계의 동작 확인은 입력·전송·초기화·반응형 배치를 보는 것이다. Gemma 답변 생성과 대화 영구 저장까지 구현됐다는 뜻이 아니다.

Target state · 미구현

Streaming 연결 후 목표로 하는 데이터 변화

채팅 UI의 핵심은 메시지 배열과 생성 상태다. 예를 들어 초기 state가 []이고 사용자가 “안녕”을 보내면 사용자 메시지가 추가된다. 서버가 streaming을 시작하면 비어 있는 assistant 메시지를 만들고, 도착하는 text 조각을 같은 항목에 누적한다.

messages = [] → [{ role: "user", content: "안녕" }] → [..., { role: "assistant", content: "" }] → [..., { role: "assistant", content: "안" }] → [..., { role: "assistant", content: "안녕하세요" }]

위 메시지 구조와 스트리밍 단계는 향후 설계 예시이며 현재 App.tsx의 실행 결과가 아니다. 그때 isGenerating 같은 boolean state를 함께 두면 중복 전송을 막고 중지 버튼이나 loading indicator를 표시할 수 있다. 네트워크 오류가 발생하면 이미 입력한 사용자 메시지는 유지하면서 assistant 항목에 재시도 가능한 오류 상태를 표시하는 편이 좋다.

왜 문자열을 화면에 바로 덧붙이지 않고 state를 바꾸나?

React는 state를 화면의 단일 근거로 사용한다. DOM을 직접 수정하면 React가 알고 있는 값과 실제 화면이 달라져 다음 render에서 내용이 사라지거나 중복될 수 있다. “state를 변경하면 React가 화면을 갱신한다”는 한 방향 규칙을 지키면 동작을 추적하고 test하기 쉬워진다.

← 이전서비스 홈 다음 →HTML 진입점