Skip to content

[FEAT] next-intl 기반 다국어(영어/한국어) 지원 세팅#105

Merged
kimminna merged 12 commits into
developfrom
feat/web/104-i18n-setup
Jul 7, 2026
Merged

[FEAT] next-intl 기반 다국어(영어/한국어) 지원 세팅#105
kimminna merged 12 commits into
developfrom
feat/web/104-i18n-setup

Conversation

@kimminna

@kimminna kimminna commented Jul 6, 2026

Copy link
Copy Markdown
Member

ISSUE 🔗

close #104



What is this PR? 🔍

timo-web에 next-intl 기반 다국어(영어/한국어) 지원을 위한 기반 라우팅·번역 설정을 구성했습니다.

배경

  • 기존 구조: app/ 하위 라우트가 locale 구분 없이 단일 트리로 구성되어 있었고, 다국어 대응을 위한 번역 리소스나 라우팅 체계가 없었습니다.
  • 발생 문제: 이 상태로는 페이지별로 언어를 분기할 방법이 없어, 영어/한국어 지원을 시작할 수 없었습니다.
  • 해결 방향: next-intl의 App Router 통합 방식을 따라 모든 라우트를 app/[locale] 동적 세그먼트 하위로 이동하고, routing/navigation/request 설정과 middleware, 번역 메시지 파일을 추가했습니다.

의존성

  • 변경 요약: next-intl 패키지를 의존성에 추가했습니다.
  • 이유: App Router 기반 프로젝트에서 공식적으로 권장되는 i18n 라이브러리이며, 서버/클라이언트 컴포넌트 양쪽에서 번역과 라우팅을 함께 다룰 수 있습니다.

i18n 라우팅 · 설정

  • 변경 요약: i18n/routing.ts, i18n/navigation.ts, i18n/request.ts, proxy.ts(middleware), messages/en.json, messages/ko.json을 추가하고 next.config.jsnext-intl/plugin을 연결했습니다.
  • 이유: next-intl은 라우팅 정의(routing), locale-aware 네비게이션 훅, 서버 요청별 locale/메시지 로딩 설정을 분리해서 구성하도록 요구합니다.
  • 구현 방식: routing.ts에서 locales: ["en", "ko"], defaultLocale: "en", localePrefix: "always"를 정의해 en, ko 모두 URL에 프리픽스가 명시적으로 붙도록 했습니다(/en/..., /ko/...). 처음에는 as-needed(기본 locale은 프리픽스 없음)로 구성했지만, 개발 중 두 로케일 경로 모두에 리다이렉트 없이 직접 접근해서 검증할 수 있어야 한다는 요구사항 때문에 always로 변경했습니다. navigation.ts는 이 routing 설정을 기반으로 Link, redirect, usePathname, useRouter, getPathname을 재노출해 이후 컴포넌트에서 locale-aware 네비게이션을 쓸 수 있게 했습니다. request.tshasLocale로 요청 locale을 검증하고, 유효하지 않으면 defaultLocale로 폴백한 뒤 messages/{locale}.json을 동적 import합니다. proxy.tsnext-intl/middlewarecreateMiddleware(routing)을 그대로 사용하고, matcher에서 api, _next, 확장자가 있는 정적 파일 경로를 제외했습니다.
  • 경계 · 제약: 번역 메시지는 현재 사이드바 내비게이션 라벨(Navigation 네임스페이스: home/today/focus/statistics/settings)만 채워둔 상태이며, 나머지 페이지의 실제 텍스트를 메시지 키로 옮기는 작업은 이번 PR 범위 밖입니다.

라우트 구조 이동

  • 변경 요약: app/ 바로 아래 있던 라우트를 app/[locale]/ 하위로 이동했습니다. 그중 home, focus, today, statistics, settings(+account, policy)는 develop에 먼저 도입된 (main) 라우트 그룹(공용 사이드바 레이아웃)과 합쳐 app/[locale]/(main)/ 하위로, login·onboardingapp/[locale]/ 바로 아래로 정리했습니다.
  • 이유: next-intl의 App Router 통합은 locale을 URL 세그먼트로 다루기 때문에, 모든 페이지가 [locale] 동적 세그먼트 하위에 있어야 middleware가 해석한 locale을 params.locale로 받을 수 있습니다. (main) 그룹은 라우팅에 영향을 주지 않으면서 사이드바가 있는 페이지들만 묶어 레이아웃을 공유하기 위한 것이라, [locale] 세그먼트 안쪽에 그대로 중첩하는 것이 맞습니다.
  • 구현 방식: 폴더 위치만 이동했고 페이지 내부 로직은 변경하지 않았습니다. app/[locale]/layout.tsx가 기존 root layout 역할을 이어받아 generateStaticParamsrouting.locales 각각에 대한 정적 경로를 생성하고, params.localerouting.locales에 없으면 notFound()를 호출합니다. html lang 속성도 이 locale 값을 사용하도록 했고, NextIntlClientProvider로 하위 트리를 감싸 클라이언트 컴포넌트에서 useTranslations를 쓸 수 있게 했습니다. 기존 app/layout.tsx, app/page.tsx는 역할이 [locale] 하위 파일로 옮겨졌으므로 제거했습니다. 폰트 파일(PretendardVariable.woff2)은 [locale] 세그먼트에 종속시키지 않기 위해 apps/timo-web/fonts/(앱 루트)로 옮기고 layout.tsx에서 상대 경로로 참조합니다. app/global-error.tsx는 Next.js 규칙상 root layout의 에러까지 잡아야 해서 [locale] 밖 최상위에 그대로 유지했습니다.
  • 경계 · 제약: [locale] 세그먼트 바깥에는 global-error.tsxapi 라우트 정도만 남아 있어야 하며, 이번 PR에는 해당하는 다른 라우트가 없어 별도 조정은 없었습니다.

사이드바 내비게이션 번역 적용

  • 변경 요약: components/layout/sidebar/NavigationSidebar.tsx에서 next/link, next/navigationusePathname 대신 @/i18n/navigationLink, usePathname을 사용하도록 바꾸고, 하드코딩돼 있던 한국어 라벨(, 오늘, 집중 모드, 통계, 설정)을 useTranslations("Navigation") 기반 번역으로 대체했습니다.
  • 이유: localePrefix: "always" 하에서 next/link를 그대로 쓰면 현재 locale 프리픽스가 붙지 않아 링크를 클릭할 때 언어가 초기화되는 문제가 있어, locale-aware 버전으로 바꿔야 합니다. 라벨도 번역 없이 하드코딩돼 있으면 en 로케일에서도 한국어가 그대로 노출되므로 함께 고쳤습니다.

import 경로 정리

  • 변경 요약: i18n/navigation.ts, i18n/request.ts, proxy.ts에서 routing을 가져오는 import를 상대 경로(./routing, ./i18n/routing)에서 @/i18n/routing 절대 경로로 통일했습니다.
  • 이유: 파일 위치가 달라져도 import 경로가 흔들리지 않도록 프로젝트에서 이미 쓰고 있는 @/ 절대 경로 alias 규칙을 그대로 따랐습니다.



사용 방법 🛠️

다른 페이지·컴포넌트에서 이번에 추가된 i18n 설정을 실제로 어떻게 쓰면 되는지 정리했습니다.

1. 번역 텍스트 추가하기

messages/en.json, messages/ko.json에 같은 키 구조로 문구를 추가합니다. 페이지·기능 단위로 네임스페이스(최상위 키)를 나눠서 관리합니다. 실제로 지금 들어 있는 Navigation 네임스페이스가 예시입니다.

// messages/en.json
{
  "Navigation": {
    "home": "Home",
    "today": "Today",
    "focus": "Focus Mode",
    "statistics": "Statistics",
    "settings": "Settings"
  }
}
// messages/ko.json
{
  "Navigation": {
    "home": "",
    "today": "오늘",
    "focus": "집중 모드",
    "statistics": "통계",
    "settings": "설정"
  }
}

두 파일의 키 구조가 어긋나면(한쪽에만 키가 있으면) 런타임에서 해당 언어로 봤을 때 문구가 비거나 네임스페이스.키 형태의 텍스트가 그대로 노출될 수 있으니, 키를 추가할 때는 항상 두 파일에 동시에 반영합니다.

2. 컴포넌트에서 번역 사용하기

서버 컴포넌트, 클라이언트 컴포넌트 모두 useTranslations를 그대로 쓸 수 있습니다. 네임스페이스(위에서 정한 최상위 키)를 인자로 넘기고, t("키")로 문구를 꺼냅니다.

// 클라이언트 컴포넌트 (NavigationSidebar 예시)
"use client";
import { useTranslations } from "next-intl";

export const NavigationSidebar = () => {
  const t = useTranslations("Navigation");
  return <span>{t("home")}</span>;
};

클라이언트 컴포넌트는 app/[locale]/layout.tsxNextIntlClientProvider가 상위에서 감싸고 있어야 동작합니다. 현재 root layout에서 이미 전체 트리를 감싸고 있어 별도 설정 없이 바로 사용 가능합니다.

3. 페이지 이동은 next/link, next/navigation 대신 @/i18n/navigation을 사용

next/linkLinknext/navigationuseRouter, usePathname, redirect를 그대로 쓰면 현재 locale 프리픽스가 자동으로 붙지 않아 언어가 섞인 링크가 생길 수 있습니다. 대신 i18n/navigation.ts에서 내보낸 버전을 사용합니다(NavigationSidebar.tsx가 실제 적용 예시입니다).

import { Link, useRouter } from "@/i18n/navigation";

function NavExample() {
  const router = useRouter();

  return (
    <>
      {/* locale이 자동으로 붙은 경로로 이동 (예: 한국어면 /ko/settings) */}
      <Link href="/settings">설정</Link>
      <button onClick={() => router.push("/login")}>로그인 페이지로</button>
    </>
  );
}

4. 새 언어(locale) 추가하기

  1. i18n/routing.tslocales 배열에 locale 코드를 추가합니다 (예: ["en", "ko", "ja"]).
  2. messages/{locale}.json 파일을 새로 만들고, 기존 en.json과 동일한 키 구조로 번역을 채웁니다.
  3. defaultLocale을 바꾸지 않는 한 나머지 설정(navigation.ts, request.ts, proxy.ts)은 수정할 필요가 없습니다 — 모두 routing을 참조하는 구조라 자동으로 새 locale을 인식합니다.

5. URL 규칙

localePrefix: "always" 설정

  • 모든 언어가 프리픽스를 가집니다 → /en, /en/settings, /ko, /ko/settings
  • 프리픽스 없는 경로(/, /settings)로 접속하면 미들웨어가 감지한 로케일(쿠키 → Accept-LanguagedefaultLocale 순)로 리다이렉트됩니다. 리다이렉트 시 NEXT_LOCALE 쿠키가 내려가서, 이후 프리픽스 없는 경로로 다시 접속해도 사용자가 최근에 머문 로케일이 유지됩니다.

새 페이지를 추가할 때 이 규칙을 그대로 따르면 되고, 별도로 언어별 라우트를 분기할 필요는 없습니다.



To Reviewers

라우트 이동은 위치 변경만 있고 페이지 컴포넌트 내부 로직은 그대로입니다(단, NavigationSidebar의 링크·라벨 부분은 실제로 수정했습니다). 실제로 봐주셨으면 하는 부분은 다음과 같습니다.

  • i18n/routing.tslocalePrefix: "always" 선택이 프로젝트의 URL 정책과 맞는지 — 개발 편의(두 로케일 직접 접근)를 위해 as-needed에서 바꿨는데, 프로덕션에서도 이 정책을 유지할지 논의가 필요합니다.
  • [locale]/layout.tsxnotFound() 폴백 방식이 기대하는 동작인지.
  • app/[locale]/(main)/ 구조로 develop의 라우트 그룹과 합친 방식이 맞는지, NavigationSidebar의 링크가 @/i18n/navigation 기반으로 바뀐 부분이 맞는지.

messages/*.json에는 아직 사이드바 라벨(Navigation)만 채워져 있고, 나머지 페이지의 텍스트를 번역 키로 옮기는 작업은 후속 PR로 남겨두었습니다.



Screenshot 📷

image image



Test Checklist ✔

  • pnpm check-types 통과
  • pnpm lint 통과
  • pnpm build 통과
  • /en, /ko 등 locale 라우팅 동작 확인 (curl로 리다이렉트·쿠키 동작 검증)

kimminna added 3 commits July 7, 2026 01:47
- next-intl 패키지를 의존성에 추가했습니다
- locale 라우팅, 미들웨어, 번역 리소스, root layout의 NextIntlClientProvider 배선을 추가했습니다
- 영어를 기본 locale로, as-needed 프리픽스 전략을 적용했습니다
- i18n 라우팅 적용을 위해 기존 라우트 폴더들을 locale 동적 세그먼트 하위로 이동했습니다
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
timo Ready Ready Preview, Comment Jul 7, 2026 6:44am

@github-actions github-actions Bot requested review from jjangminii and yumin-kim2 July 6, 2026 16:56
@github-actions github-actions Bot added ⏰ Timo-web Timo 웹 서비스 ✨ Feature 새로운 기능(기능성) 구현 ♦️ 민아 민아상 labels Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

next-intl 기반 i18n 지원을 위해 routing/navigation/request 설정, locale 감지 미들웨어, next.config.js 플러그인 연동을 추가했습니다. 기존 루트 레이아웃을 삭제하고 locale-aware 레이아웃/홈 페이지를 신설했으며, 사이드바 라벨을 번역 키로 전환하고 home 관련 파일들의 import 경로를 [locale] 세그먼트로 마이그레이션했습니다.

Changes

next-intl i18n 세팅

Layer / File(s) Summary
i18n 라우팅과 요청 처리
apps/timo-web/i18n/routing.ts, apps/timo-web/i18n/navigation.ts, apps/timo-web/i18n/request.ts, apps/timo-web/proxy.ts, apps/timo-web/global.d.ts
routing(en/ko, localePrefix always) 정의, navigation 헬퍼 재내보내기, requestLocale 검증 후 메시지 동적 로딩, 미들웨어 matcher 설정, AppConfig 모듈 보강이 추가됨.
locale 레이아웃, 홈 페이지, 번역 리소스
apps/timo-web/app/[locale]/layout.tsx, apps/timo-web/app/[locale]/page.tsx, apps/timo-web/app/layout.tsx(삭제), apps/timo-web/messages/en.json, apps/timo-web/messages/ko.json, apps/timo-web/next.config.js, apps/timo-web/package.json
기존 루트 레이아웃 제거 후 generateStaticParams/hasLocale 검증/NextIntlClientProvider+QueryProvider를 갖춘 locale 레이아웃 신설, 홈 페이지는 빈 fragment 반환, Navigation 메시지 리소스 추가, next.config.js에 withNextIntl 연동, next-intl 의존성 추가.
네비게이션 사이드바 번역 적용
apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx
next/link/next/navigation 대신 @/i18n/navigationLink/usePathname 사용, NAV_ITEMSlabellabelKey로 전환해 useTranslations로 번역된 라벨 렌더링.
home 라우트 경로 마이그레이션
apps/timo-web/app/[locale]/(main)/home/_components/HomeTodoCard.tsx, .../_containers/HomeTodoContainer.tsx, .../_mocks/todo-mock.ts, .../home/page.tsx
@/app/(main)/... import 경로를 @/app/[locale]/...로 일괄 수정, 로직 변경 없음.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Proxy as proxy.ts (middleware)
  participant Layout as app/[locale]/layout.tsx
  participant Request as i18n/request.ts
  participant Messages as messages/*.json

  Browser->>Proxy: /home 요청
  Proxy->>Proxy: routing 기반 locale 결정
  Proxy-->>Browser: /[locale]/home 리다이렉트
  Browser->>Layout: /[locale]/home 요청
  Layout->>Layout: hasLocale로 유효성 검사
  Layout->>Request: requestLocale 전달
  Request->>Messages: locale 메시지 동적 import
  Messages-->>Request: messages
  Request-->>Layout: locale, messages
  Layout-->>Browser: NextIntlClientProvider로 감싼 페이지 렌더링
Loading

Possibly related PRs

  • Team-Timo/Timo-client#21: 기존 apps/timo-web/app/layout.tsxQueryProvider 래핑 로직을 삭제하고 app/[locale]/layout.tsx로 대체하는 지점이 겹칩니다.
  • Team-Timo/Timo-client#27: next.config.jswithSentryConfig 적용부에 withNextIntl을 추가한 지점이 동일 코드 영역을 수정합니다.
  • Team-Timo/Timo-client#102: NavigationSidebar.tsx의 경로 선택/네비게이션 항목 렌더링 로직을 함께 수정합니다.

Suggested labels: 🛠️ Setup

Suggested reviewers: ehye1, yumin-kim2


지역화 코드 리뷰 한 줄 촌평: 하드코딩된 "Hi Timo" 인사말을 걷어내고 다국어 인프라를 깔끔하게 놓으셨네요, 굿잡! 🐰🌏 다만 이슈에 명시된 localePrefix: "as-needed" 대신 "always"로 설정된 부분은 확인이 필요해 보여요 — next-intl 라우팅 문서를 참고해 정책을 명확히 맞춰주시면 좋겠습니다.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 대부분 맞지만 이슈의 as-needed URL 프리픽스 요구와 달리 localePrefix: "always"로 구현되어 핵심 요구를 벗어납니다. routing.ts의 localePrefixas-needed로 되돌리고 영어는 무프리픽스, 한국어는 /ko/...가 되도록 경로와 링크를 맞추세요.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 next-intl 기반 영어/한국어 다국어 지원 세팅을 잘 요약해 PR의 핵심 변경과 일치합니다.
Description check ✅ Passed 설명이 라우팅, 번역 리소스, 미들웨어, 레이아웃, 플러그인 연결까지 변경 범위를 잘 담고 있어 관련성이 높습니다.
Out of Scope Changes check ✅ Passed 변경은 i18n 기반 라우팅·번역·레이아웃에 집중돼 있어, 별도의 무관한 코드가 보이지 않습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web/104-i18n-setup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Storybook Preview

항목 링크
Storybook 열기
Chromatic 빌드 확인

마지막 업데이트: 2026-07-07 06:44 UTC

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale] 0 B 🟡 205.59 kB
/[locale]/focus 0 B 🟡 205.59 kB
/[locale]/home 0 B 🟡 205.59 kB
/[locale]/login 0 B 🟡 205.59 kB
/[locale]/onboarding 0 B 🟡 205.59 kB
/[locale]/settings 0 B 🟡 205.59 kB
/[locale]/settings/account 0 B 🟡 205.59 kB
/[locale]/settings/policy 0 B 🟡 205.59 kB
/[locale]/statistics 0 B 🟡 205.59 kB
/[locale]/today 0 B 🟡 205.59 kB

공유 번들: 205.59 kB
🟢 < 200kB  |  🟡 < 350kB  |  🔴 ≥ 350kB (First Load JS · gzip)

Lighthouse — timo-web

⚠️ Lighthouse 결과를 가져오지 못했습니다.

Image Optimization — timo-web

public/ 디렉토리에 이미지가 없습니다.

측정 커밋: 37b25d4

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/timo-web/app/`[locale]/page.tsx:
- Around line 3-7: The Home component currently renders the translated title
inside a Fragment, so it lacks the required heading semantics. Update the Home
component to wrap the result of useTranslations("HomePage") in a single h1
element, keeping only one h1 on the page and ensuring the homepage title is
exposed as the primary heading for accessibility and SEO.

In `@apps/timo-web/package.json`:
- Line 20: The dependency version policy is inconsistent in package.json: next
is pinned exactly, but next-intl still uses a caret range. Update the next-intl
entry to match the repo’s pinning approach by using an exact version, and keep
this change aligned with the existing dependency declarations in the same
package manifest.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0c155641-886f-4d84-a4eb-6a28453d6523

📥 Commits

Reviewing files that changed from the base of the PR and between e12b5af and 8a3b6f5.

⛔ Files ignored due to path filters (2)
  • apps/timo-web/app/[locale]/fonts/PretendardVariable.woff2 is excluded by !**/*.woff2
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • apps/timo-web/app/[locale]/focus/_components/.gitkeep
  • apps/timo-web/app/[locale]/focus/_containers/.gitkeep
  • apps/timo-web/app/[locale]/focus/_queries/.gitkeep
  • apps/timo-web/app/[locale]/focus/page.tsx
  • apps/timo-web/app/[locale]/globals.css
  • apps/timo-web/app/[locale]/home/_components/.gitkeep
  • apps/timo-web/app/[locale]/home/_containers/.gitkeep
  • apps/timo-web/app/[locale]/home/_queries/.gitkeep
  • apps/timo-web/app/[locale]/home/page.tsx
  • apps/timo-web/app/[locale]/layout.tsx
  • apps/timo-web/app/[locale]/login/page.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/.gitkeep
  • apps/timo-web/app/[locale]/onboarding/_containers/.gitkeep
  • apps/timo-web/app/[locale]/onboarding/_queries/.gitkeep
  • apps/timo-web/app/[locale]/onboarding/page.tsx
  • apps/timo-web/app/[locale]/page.tsx
  • apps/timo-web/app/[locale]/settings/_components/.gitkeep
  • apps/timo-web/app/[locale]/settings/_containers/.gitkeep
  • apps/timo-web/app/[locale]/settings/_queries/.gitkeep
  • apps/timo-web/app/[locale]/settings/account/_components/.gitkeep
  • apps/timo-web/app/[locale]/settings/account/_containers/.gitkeep
  • apps/timo-web/app/[locale]/settings/account/_queries/.gitkeep
  • apps/timo-web/app/[locale]/settings/account/page.tsx
  • apps/timo-web/app/[locale]/settings/layout.tsx
  • apps/timo-web/app/[locale]/settings/page.tsx
  • apps/timo-web/app/[locale]/settings/policy/_components/.gitkeep
  • apps/timo-web/app/[locale]/settings/policy/_containers/.gitkeep
  • apps/timo-web/app/[locale]/settings/policy/_queries/.gitkeep
  • apps/timo-web/app/[locale]/settings/policy/page.tsx
  • apps/timo-web/app/[locale]/statistics/_components/.gitkeep
  • apps/timo-web/app/[locale]/statistics/_containers/.gitkeep
  • apps/timo-web/app/[locale]/statistics/_queries/.gitkeep
  • apps/timo-web/app/[locale]/statistics/page.tsx
  • apps/timo-web/app/[locale]/today/_components/.gitkeep
  • apps/timo-web/app/[locale]/today/_containers/.gitkeep
  • apps/timo-web/app/[locale]/today/_queries/.gitkeep
  • apps/timo-web/app/[locale]/today/page.tsx
  • apps/timo-web/app/layout.tsx
  • apps/timo-web/app/page.tsx
  • apps/timo-web/i18n/navigation.ts
  • apps/timo-web/i18n/request.ts
  • apps/timo-web/i18n/routing.ts
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
  • apps/timo-web/next.config.js
  • apps/timo-web/package.json
  • apps/timo-web/proxy.ts
💤 Files with no reviewable changes (2)
  • apps/timo-web/app/page.tsx
  • apps/timo-web/app/layout.tsx

Comment thread apps/timo-web/app/[locale]/page.tsx
Comment thread apps/timo-web/package.json
- navigation.ts, request.ts, proxy.ts에서 상대 경로로 되어 있던 routing import를 @/i18n/routing 절대 경로로 통일했습니다
develop의 (main) 라우트 그룹 구조를 이 브랜치의 next-intl [locale] 세그먼트와
결합하여 app/[locale]/(main)/{focus,home,settings,statistics,today} 구조로
재정렬한다. NavigationSidebar는 로케일 인식 라우팅을 위해 next/link 대신
@/i18n/navigation의 Link/usePathname을 사용하도록 수정하고, 폰트 경로를
apps/timo-web/fonts로 옮겨진 실제 위치에 맞춰 수정한다.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/timo-web/proxy.ts (1)

7-9: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

matcher에 _vercel 제외 패턴 추가를 검토해주세요! 🕵️

현재 matcher는 api, _next, 확장자 포함 경로만 제외하고 있는데, next-intl 공식 문서는 Vercel 배포 시 /_vercel/... 내부 rewrite 경로도 함께 제외하도록 권장합니다. 이 앱이 Vercel에 배포된다면 Analytics/Insights 같은 내부 엔드포인트가 의도치 않게 locale 미들웨어에 걸릴 수 있습니다.

🔧 제안하는 수정
 export const config = {
-  matcher: ["/((?!api|_next|.*\\..*).*)"],
+  matcher: ["/((?!api|trpc|_next|_vercel|.*\\..*).*)"],
 };

관련 문서: next-intl Proxy/Middleware 가이드

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/proxy.ts` around lines 7 - 9, The proxy matcher in config
should also exclude Vercel internal routes so locale middleware doesn’t
intercept them. Update the matcher in proxy.ts to keep the existing api, _next,
and static-extension exclusions while adding an explicit _vercel exclusion
pattern, following the next-intl Proxy/Middleware guidance. Keep the change
localized to the exported config.matcher definition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx:
- Around line 84-94: TodoCreateModal mixes hardcoded English and Korean strings,
and its UI text is not wired to next-intl. Move all visible labels and
aria-labels in TodoCreateModal to useTranslations so they resolve from locale
messages consistently, including the button/title text and the modal controls
referenced by the current hardcoded strings. Make sure the component uses
translation keys for the strings currently shown in the modal so locale changes
affect every label uniformly.
- Around line 135-142: The TodoCreateModal textarea currently uses outline-none
without any replacement focus indicator, which violates the accessibility rule.
Update the textarea in TodoCreateModal to keep a visible keyboard focus state by
adding a proper focus-visible style such as a ring or border while removing the
raw outline suppression only if replaced. Make sure the change preserves clear
focus feedback for the textarea element and follows the app’s accessibility
convention for tsx components.
- Line 20: Rename the union type alias `TodoSubtaskVariant` to
`TodoSubtaskVariantTypes` to match the `Types` suffix convention for union
aliases. Update every reference that uses it, including
`TodoSubtaskItem.variant`, `SUBTASK_LABEL_STYLE`, and the `subtasks.map` usage
in `TodoCreateModal`, so the new type name is used consistently throughout the
component.
- Line 1: TodoCreateModal is a client-side, event-handling component and should
not live under the pure presentational _components_ layer. Move the logic in
TodoCreateModal to an appropriate _containers_ location and keep only
props-driven UI in _components_, preserving the existing client behavior by
keeping the component’s onClick/onChange handling in the container-level entry
point.

In `@apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx`:
- Around line 68-69: Rename the boolean helper in NavigationSidebar from
isActivePath to a convention-compliant name such as checkIsActivePath, and
update every call site in the same component to use the new identifier. Keep the
logic in the helper unchanged; this is only a naming consistency fix for the
pathname/href active-state check.

---

Outside diff comments:
In `@apps/timo-web/proxy.ts`:
- Around line 7-9: The proxy matcher in config should also exclude Vercel
internal routes so locale middleware doesn’t intercept them. Update the matcher
in proxy.ts to keep the existing api, _next, and static-extension exclusions
while adding an explicit _vercel exclusion pattern, following the next-intl
Proxy/Middleware guidance. Keep the change localized to the exported
config.matcher definition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5d3433d4-a8d7-496c-a21c-c2dad56eff57

📥 Commits

Reviewing files that changed from the base of the PR and between 8a3b6f5 and 3f670fb.

⛔ Files ignored due to path filters (1)
  • apps/timo-web/fonts/PretendardVariable.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (36)
  • apps/timo-web/app/[locale]/(main)/focus/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/focus/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/focus/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/focus/page.tsx
  • apps/timo-web/app/[locale]/(main)/home/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx
  • apps/timo-web/app/[locale]/(main)/home/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/home/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/home/page.tsx
  • apps/timo-web/app/[locale]/(main)/layout.tsx
  • apps/timo-web/app/[locale]/(main)/settings/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/account/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/account/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/account/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/account/page.tsx
  • apps/timo-web/app/[locale]/(main)/settings/layout.tsx
  • apps/timo-web/app/[locale]/(main)/settings/page.tsx
  • apps/timo-web/app/[locale]/(main)/settings/policy/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/policy/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/policy/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/policy/page.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/statistics/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/statistics/page.tsx
  • apps/timo-web/app/[locale]/(main)/today/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/today/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/today/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/today/page.tsx
  • apps/timo-web/app/[locale]/layout.tsx
  • apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx
  • apps/timo-web/i18n/navigation.ts
  • apps/timo-web/i18n/request.ts
  • apps/timo-web/proxy.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/timo-web/proxy.ts (1)

7-9: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

matcher에 _vercel 제외 패턴 추가를 검토해주세요! 🕵️

현재 matcher는 api, _next, 확장자 포함 경로만 제외하고 있는데, next-intl 공식 문서는 Vercel 배포 시 /_vercel/... 내부 rewrite 경로도 함께 제외하도록 권장합니다. 이 앱이 Vercel에 배포된다면 Analytics/Insights 같은 내부 엔드포인트가 의도치 않게 locale 미들웨어에 걸릴 수 있습니다.

🔧 제안하는 수정
 export const config = {
-  matcher: ["/((?!api|_next|.*\\..*).*)"],
+  matcher: ["/((?!api|trpc|_next|_vercel|.*\\..*).*)"],
 };

관련 문서: next-intl Proxy/Middleware 가이드

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/proxy.ts` around lines 7 - 9, The proxy matcher in config
should also exclude Vercel internal routes so locale middleware doesn’t
intercept them. Update the matcher in proxy.ts to keep the existing api, _next,
and static-extension exclusions while adding an explicit _vercel exclusion
pattern, following the next-intl Proxy/Middleware guidance. Keep the change
localized to the exported config.matcher definition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx:
- Around line 84-94: TodoCreateModal mixes hardcoded English and Korean strings,
and its UI text is not wired to next-intl. Move all visible labels and
aria-labels in TodoCreateModal to useTranslations so they resolve from locale
messages consistently, including the button/title text and the modal controls
referenced by the current hardcoded strings. Make sure the component uses
translation keys for the strings currently shown in the modal so locale changes
affect every label uniformly.
- Around line 135-142: The TodoCreateModal textarea currently uses outline-none
without any replacement focus indicator, which violates the accessibility rule.
Update the textarea in TodoCreateModal to keep a visible keyboard focus state by
adding a proper focus-visible style such as a ring or border while removing the
raw outline suppression only if replaced. Make sure the change preserves clear
focus feedback for the textarea element and follows the app’s accessibility
convention for tsx components.
- Line 20: Rename the union type alias `TodoSubtaskVariant` to
`TodoSubtaskVariantTypes` to match the `Types` suffix convention for union
aliases. Update every reference that uses it, including
`TodoSubtaskItem.variant`, `SUBTASK_LABEL_STYLE`, and the `subtasks.map` usage
in `TodoCreateModal`, so the new type name is used consistently throughout the
component.
- Line 1: TodoCreateModal is a client-side, event-handling component and should
not live under the pure presentational _components_ layer. Move the logic in
TodoCreateModal to an appropriate _containers_ location and keep only
props-driven UI in _components_, preserving the existing client behavior by
keeping the component’s onClick/onChange handling in the container-level entry
point.

In `@apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx`:
- Around line 68-69: Rename the boolean helper in NavigationSidebar from
isActivePath to a convention-compliant name such as checkIsActivePath, and
update every call site in the same component to use the new identifier. Keep the
logic in the helper unchanged; this is only a naming consistency fix for the
pathname/href active-state check.

---

Outside diff comments:
In `@apps/timo-web/proxy.ts`:
- Around line 7-9: The proxy matcher in config should also exclude Vercel
internal routes so locale middleware doesn’t intercept them. Update the matcher
in proxy.ts to keep the existing api, _next, and static-extension exclusions
while adding an explicit _vercel exclusion pattern, following the next-intl
Proxy/Middleware guidance. Keep the change localized to the exported
config.matcher definition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5d3433d4-a8d7-496c-a21c-c2dad56eff57

📥 Commits

Reviewing files that changed from the base of the PR and between 8a3b6f5 and 3f670fb.

⛔ Files ignored due to path filters (1)
  • apps/timo-web/fonts/PretendardVariable.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (36)
  • apps/timo-web/app/[locale]/(main)/focus/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/focus/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/focus/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/focus/page.tsx
  • apps/timo-web/app/[locale]/(main)/home/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx
  • apps/timo-web/app/[locale]/(main)/home/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/home/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/home/page.tsx
  • apps/timo-web/app/[locale]/(main)/layout.tsx
  • apps/timo-web/app/[locale]/(main)/settings/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/account/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/account/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/account/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/account/page.tsx
  • apps/timo-web/app/[locale]/(main)/settings/layout.tsx
  • apps/timo-web/app/[locale]/(main)/settings/page.tsx
  • apps/timo-web/app/[locale]/(main)/settings/policy/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/policy/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/policy/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/settings/policy/page.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/statistics/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/statistics/page.tsx
  • apps/timo-web/app/[locale]/(main)/today/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/today/_containers/.gitkeep
  • apps/timo-web/app/[locale]/(main)/today/_queries/.gitkeep
  • apps/timo-web/app/[locale]/(main)/today/page.tsx
  • apps/timo-web/app/[locale]/layout.tsx
  • apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx
  • apps/timo-web/i18n/navigation.ts
  • apps/timo-web/i18n/request.ts
  • apps/timo-web/proxy.ts
🛑 Comments failed to post (5)
apps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx (4)

1-1: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# _components 디렉터리 내 다른 'use client' 컴포넌트 사례가 있는지 확인
rg -n "use client" apps/timo-web/app --glob '*_components*'

Repository: Team-Timo/Timo-client

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== TodoCreateModal.tsx =="
sed -n '1,240p' apps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx

echo
echo "== nearby files =="
find apps/timo-web/app/[locale]/(main)/home -maxdepth 3 -type f | sort

echo
echo "== component/containers structure under home =="
find apps/timo-web/app/[locale]/(main)/home -maxdepth 2 -type d | sort

Repository: Team-Timo/Timo-client

Length of output: 238


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE='apps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx'
BASE='apps/timo-web/app/[locale]/(main)/home'

echo "== TodoCreateModal.tsx =="
sed -n '1,240p' "$FILE"

echo
echo "== nearby files =="
find "$BASE" -maxdepth 3 -type f | sort

echo
echo "== component/containers structure under home =="
find "$BASE" -maxdepth 2 -type d | sort

Repository: Team-Timo/Timo-client

Length of output: 6534


_components/보다는 _containers/로 옮겨주세요
이 컴포넌트는 onClick/onChange를 직접 처리하는 클라이언트 컴포넌트라, _components/의 “props만 받는 순수 UI” 규칙과는 조금 어긋납니다. 상태/이벤트 결합은 _containers/로 분리하고, _components/에는 프레젠테이션만 남기는 쪽이 구조가 더 깔끔합니다.
참고: Next.js Client Components, 컴포넌트 구조 가이드

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx
at line 1, TodoCreateModal is a client-side, event-handling component and should
not live under the pure presentational _components_ layer. Move the logic in
TodoCreateModal to an appropriate _containers_ location and keep only
props-driven UI in _components_, preserving the existing client behavior by
keeping the component’s onClick/onChange handling in the container-level entry
point.

Source: Path instructions


20-20: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

유니언 타입 별칭에 Types 접미사가 빠졌습니다.

TodoSubtaskVariant는 유니언 타입 별칭인데, 컨벤션상 Types 접미사가 필요합니다 (TodoSubtaskVariantTypes). 이름 하나로 컨벤션 경찰을 피할 수 있어요 👮.

✏️ 제안 diff
-export type TodoSubtaskVariant = "filled" | "placeholder";
+export type TodoSubtaskVariantTypes = "filled" | "placeholder";

(그리고 이 타입을 참조하는 TodoSubtaskItem.variant, SUBTASK_LABEL_STYLE, subtasks.map 내 사용처도 함께 변경 필요)

As per path instructions, "type alias는 유니언·튜플·리터럴에만 사용, 접미사 Types".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx
at line 20, Rename the union type alias `TodoSubtaskVariant` to
`TodoSubtaskVariantTypes` to match the `Types` suffix convention for union
aliases. Update every reference that uses it, including
`TodoSubtaskItem.variant`, `SUBTASK_LABEL_STYLE`, and the `subtasks.map` usage
in `TodoCreateModal`, so the new type name is used consistently throughout the
component.

Source: Path instructions


84-94: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

영어/한국어 텍스트가 뒤섞여 있고, next-intl 연동이 빠져 있어요.

"Add a task", "Add an icon", "Please enter a note..", "Create" 같은 영어 UI 텍스트와 aria-label="닫기", aria-label="우선순위 설정", 노트 같은 한국어 라벨이 한 컴포넌트에 공존합니다. 이번 PR이 next-intl 기반 다국어 지원을 도입하는 목적이니, 이 모달의 문자열들도 useTranslations로 옮기면 좋을 것 같습니다. 지금 상태로는 로케일이 ko여도 화면엔 영어가 그대로 나올 텐데, 이건 국제화라기보단 "국제 짬뽕화"에 가깝네요 🍜.

PR 설명에 따르면 메시지 마이그레이션은 후속 PR에서 진행 예정이라고 하니, 이 컴포넌트도 해당 후속 작업 범위에 포함되는지 확인 부탁드립니다.

Also applies to: 106-108, 132-140, 154-156, 166-167, 173-173, 183-183, 187-187, 193-193

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx
around lines 84 - 94, TodoCreateModal mixes hardcoded English and Korean
strings, and its UI text is not wired to next-intl. Move all visible labels and
aria-labels in TodoCreateModal to useTranslations so they resolve from locale
messages consistently, including the button/title text and the modal controls
referenced by the current hardcoded strings. Make sure the component uses
translation keys for the strings currently shown in the modal so locale changes
affect every label uniformly.

135-142: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

포커스 아웃라인 제거는 접근성 가이드라인 위반입니다.

textarea에 outline-none을 적용하면서 대체 포커스 스타일이 없습니다. 키보드 사용자가 포커스 위치를 인지할 수 없게 되니, focus-visible:ring 등으로 대체 스타일을 꼭 넣어주세요. 시각적으로 깔끔한 것도 좋지만, 포커스가 사라지면 사용자는 미아가 됩니다! 🧭

♿ 제안 diff
           <textarea
             id={noteId}
             value={note}
             onChange={(e) => onNoteChange?.(e.target.value)}
             placeholder="Please enter a note.."
             rows={1}
-            className="typo-body-r-12 text-timo-gray-700 placeholder:text-timo-gray-700 w-full resize-none bg-transparent outline-none"
+            className="typo-body-r-12 text-timo-gray-700 placeholder:text-timo-gray-700 focus-visible:ring-timo-blue-300 w-full resize-none bg-transparent outline-none focus-visible:ring-2"
           />

As per path instructions, outline: none으로 포커스 테두리 제거 금지 (**/*.{ts,tsx} 접근성 규칙).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

          <textarea
            id={noteId}
            value={note}
            onChange={(e) => onNoteChange?.(e.target.value)}
            placeholder="Please enter a note.."
            rows={1}
            className="typo-body-r-12 text-timo-gray-700 placeholder:text-timo-gray-700 focus-visible:ring-timo-blue-300 w-full resize-none bg-transparent outline-none focus-visible:ring-2"
          />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx
around lines 135 - 142, The TodoCreateModal textarea currently uses outline-none
without any replacement focus indicator, which violates the accessibility rule.
Update the textarea in TodoCreateModal to keep a visible keyboard focus state by
adding a proper focus-visible style such as a ring or border while removing the
raw outline suppression only if replaced. Make sure the change preserves clear
focus feedback for the textarea element and follows the app’s accessibility
convention for tsx components.

Source: Path instructions

apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx (1)

68-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

함수 네이밍 컨벤션 살짝 어긋났어요! ✍️

isActivePath는 boolean을 반환하는 함수인데, 컨벤션은 함수 접두사로 get/create/check/convert/filter만 허용하고 있습니다. checkIsActivePath 또는 유사한 이름으로 바꾸는 게 일관성 있을 것 같아요.

✏️ 제안
-  const isActivePath = (pathname: string, href: string) =>
+  const checkIsActivePath = (pathname: string, href: string) =>
     pathname === href || pathname.startsWith(`${href}/`);

(호출부 isActivePath(pathname, href)도 함께 이름 변경 필요)

As per path instructions, "함수 접두사: get(반환) / create(생성) / check(확인) / convert(변환) / filter(필터링)".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  const checkIsActivePath = (pathname: string, href: string) =>
    pathname === href || pathname.startsWith(`${href}/`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx` around lines
68 - 69, Rename the boolean helper in NavigationSidebar from isActivePath to a
convention-compliant name such as checkIsActivePath, and update every call site
in the same component to use the new identifier. Keep the logic in the helper
unchanged; this is only a naming consistency fix for the pathname/href
active-state check.

Source: Path instructions

kimminna added 2 commits July 7, 2026 15:06
NavigationSidebar의 하드코딩된 한국어 라벨을 messages/{en,ko}.json의
Navigation 네임스페이스로 대체하고, localePrefix를 always로 변경해
개발 중 /en, /ko 경로에 직접 접근할 수 있게 한다. 미사용 HomePage
번역 키 제거로 깨졌던 루트 page.tsx도 함께 정리한다.

@jjangminii jjangminii left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다국어 버전 지원하는건 처음인데 넥스트에서 이렇게 처리할 수가 있네요-! 신기해요

다만 두 파일의 키 구조가 어긋나도 런타임 전까지 알 수 없어서요! next-intl의 TypeScript 타입 생성 기능을 활성화하면 컴파일 타임에 잡을 수 있을 것 같은데, 추가하는 게 어떨까요? 😊
관련 공식 문서: https://next-intl.dev/docs/workflows/typescript

Comment thread apps/timo-web/proxy.ts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미들웨어 파일명이 proxy.ts인데, Next.js는 미들웨어를 middleware.ts로만 인식하는 걸로 알고 있어요. proxy.ts로는 실제로 미들웨어가 동작하지 않을 것 같아서요 middleware.ts로 rename이 필요하지 않을까요?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

middleware -> proxy로 변경이 되었다고 알고 있습니다!

@kimminna

kimminna commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

다국어 버전 지원하는건 처음인데 넥스트에서 이렇게 처리할 수가 있네요-! 신기해요

다만 두 파일의 키 구조가 어긋나도 런타임 전까지 알 수 없어서요! next-intl의 TypeScript 타입 생성 기능을 활성화하면 컴파일 타임에 잡을 수 있을 것 같은데, 추가하는 게 어떨까요? 😊 관련 공식 문서: https://next-intl.dev/docs/workflows/typescript

좋은 정보 좋습니다!
지금 같은 경우는 파일 키가 오타 나거나 누락된다면 컴파일 타임에만 잡을 수 있어서 확실히 지금 단계에서 미리 런타임 검증이 가능하게 타입 설정을 추가하면 좋을 것 같네요!

- next-intl의 AppConfig를 확장해 Locale과 Messages 타입을 선언했습니다
- 번역 키 자동완성과 컴파일 타임 타입 체크가 가능해졌습니다
kimminna added 2 commits July 7, 2026 15:32
- home 페이지 관련 컴포넌트·컨테이너·목업·타입·유틸을 (main) 하위에서 [locale]/(main) 하위로 이동했습니다
- 이동에 따라 변경된 절대 경로 import를 수정했습니다
- [locale] 라우트 그룹의 빈 플레이스홀더(.gitkeep) 파일을 제거했습니다
- [locale]/layout.tsx로 대체된 기존 root layout.tsx를 삭제했습니다

@yumin-kim2 yumin-kim2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[locale] 세그먼트 도입과 라우트 그룹((main)) 병합이 자연스럽게 잘 되어있네용 👍
@/i18n/navigation으로 통일해서 링크 관련 이슈 미리 막아주신 부분도 너무 좋습니다!!

이런 다국어 지원은 기능은 처음 접해보는데 PR 설명이 꼼꼼하고 사용법 가이드도 남겨주셔서 편할 것 같아요 많은 도움이 됐습니다 고생하셨서요~~!!

@kimminna kimminna merged commit 63a58ee into develop Jul 7, 2026
12 of 13 checks passed
@kimminna kimminna deleted the feat/web/104-i18n-setup branch July 7, 2026 06:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/timo-web/app/`[locale]/(main)/home/_mocks/todo-mock.ts:
- Line 1: The Todo import in the todo-mock module is type-only but is currently
written as a value import, so update the existing import to use type-only syntax
for consistency and better bundling. Keep the change localized to the Todo
import in todo-mock and match the pattern already used in HomeTodoContainer.tsx
by making the import explicitly type-only.

In `@apps/timo-web/i18n/routing.ts`:
- Line 6: The localePrefix setting in the routing configuration is still set to
always, which conflicts with the requirement to use as-needed from the start and
avoid an /en prefix on English routes. Update the localePrefix value in the
routing setup to as-needed and remove or revise the TODO comment so the behavior
matches the intended i18n routing policy.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 30874366-af13-4f86-bdc4-49dbeae62b3d

📥 Commits

Reviewing files that changed from the base of the PR and between 3f670fb and 1dcd2f8.

📒 Files selected for processing (14)
  • apps/timo-web/app/[locale]/(main)/home/_components/HomeDateInformation.tsx
  • apps/timo-web/app/[locale]/(main)/home/_components/HomeTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/home/_containers/HomeTodoContainer.tsx
  • apps/timo-web/app/[locale]/(main)/home/_mocks/todo-mock.ts
  • apps/timo-web/app/[locale]/(main)/home/_types/todo-type.ts
  • apps/timo-web/app/[locale]/(main)/home/_utils/date.ts
  • apps/timo-web/app/[locale]/(main)/home/_utils/todo-time.ts
  • apps/timo-web/app/[locale]/(main)/home/page.tsx
  • apps/timo-web/app/[locale]/page.tsx
  • apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx
  • apps/timo-web/global.d.ts
  • apps/timo-web/i18n/routing.ts
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json

@@ -1,4 +1,4 @@
import { Todo } from "@/app/(main)/home/_types/todo-type";
import { Todo } from "@/app/[locale]/(main)/home/_types/todo-type";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

타입 전용 import에는 import type을 붙여주세요.

Todo는 타입으로만 쓰이는데, 같은 계층의 HomeTodoContainer.tsx(Line 6)는 import type { Todo }를 쓰는 반면 여기는 값 import 구문을 그대로 쓰고 있어요. 사소하지만 번들 최적화와 일관성을 위해 맞춰주시면 좋을 것 같습니다.

💡 제안
-import { Todo } from "`@/app/`[locale]/(main)/home/_types/todo-type";
+import type { Todo } from "`@/app/`[locale]/(main)/home/_types/todo-type";

관련 문서: TypeScript - import type

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { Todo } from "@/app/[locale]/(main)/home/_types/todo-type";
import type { Todo } from "`@/app/`[locale]/(main)/home/_types/todo-type";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/app/`[locale]/(main)/home/_mocks/todo-mock.ts at line 1, The
Todo import in the todo-mock module is type-only but is currently written as a
value import, so update the existing import to use type-only syntax for
consistency and better bundling. Keep the change localized to the Todo import in
todo-mock and match the pattern already used in HomeTodoContainer.tsx by making
the import explicitly type-only.

export const routing = defineRouting({
locales: ["en", "ko"],
defaultLocale: "en",
localePrefix: "always", // as-needed 로 추후 변경

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

localePrefix 설정이 PR 목표와 다릅니다 — TODO 코멘트 확인 필요.

주석에 as-needed 로 추후 변경이라고 적혀 있는데, 연동된 이슈 요구사항은 처음부터 en prefix 없이 as-needed로 동작해야 한다고 명시하고 있어요. 지금 always로 두면 /en/...처럼 영어 경로에도 항상 prefix가 붙어서 요구사항과 어긋납니다.

만약 스택의 다음 PR에서 바로 고칠 계획이라면 괜찮지만, 그렇지 않다면 지금 반영해주시는 게 좋을 것 같아요. 이 TODO, 제가 대신 처리해드릴까요? 아니면 이슈로 등록해드릴까요?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/i18n/routing.ts` at line 6, The localePrefix setting in the
routing configuration is still set to always, which conflicts with the
requirement to use as-needed from the start and avoid an /en prefix on English
routes. Update the localePrefix value in the routing setup to as-needed and
remove or revise the TODO comment so the behavior matches the intended i18n
routing policy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능(기능성) 구현 ⏰ Timo-web Timo 웹 서비스 ♦️ 민아 민아상

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] i18n(영어/한국어) 다국어 지원 기반 세팅

3 participants