Skip to content

[feat] 광고 배너 추가#200

Merged
ff1451 merged 4 commits intodevelopfrom
195-feat-광고-배너-추가
Mar 20, 2026

Hidden character warning

The head ref may contain hidden characters: "195-feat-\uad11\uace0-\ubc30\ub108-\ucd94\uac00"
Merged

[feat] 광고 배너 추가#200
ff1451 merged 4 commits intodevelopfrom
195-feat-광고-배너-추가

Conversation

@ff1451
Copy link
Copy Markdown
Collaborator

@ff1451 ff1451 commented Mar 20, 2026

Summary by CodeRabbit

새로운 기능

  • 클럽 목록 및 채팅 목록에 광고 카드 표시 기능 추가
  • 일정한 간격으로 광고 배치 (기본값: 4개 항목마다 1개 광고)
  • 광고 클릭 이벤트 추적 기능 구현
  • 뷰포트 크기 변화에 자동으로 대응하는 반응형 광고 배치 시스템

@ff1451 ff1451 self-assigned this Mar 20, 2026
@ff1451 ff1451 linked an issue Mar 20, 2026 that may be closed by this pull request
@ff1451 ff1451 requested a review from Copilot March 20, 2026 12:37
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 20, 2026

Walkthrough

광고 표시 기능을 추가하는 PR입니다. Advertisement API 엔티티와 요청/응답 타입을 정의하고, 광고 조회 및 클릭 추적 API 함수를 구현했습니다. 레이아웃 요소들(main, bottomNav)의 refs를 제공하는 Context를 새로 추가했고, Chat/Club 리스트에 광고를 주기적으로 삽입하는 UI 로직을 구현했습니다. 광고 간격을 계산하고 관리하는 두 개의 커스텀 훅을 추가했습니다.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 광고 배너 추가라는 PR의 주요 변경사항을 명확하게 요약하고 있으며, 간결하고 구체적입니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 195-feat-광고-배너-추가
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (7)
src/pages/Club/ClubList/components/AdvertisementCard.tsx (1)

25-29: 하드코딩된 색상값 대신 테마 토큰 사용 권장

#1E2C3F, #EAF6FB, #2F6E83 등 하드코딩된 색상값이 사용되고 있습니다. 코딩 가이드라인에 따라 src/styles/theme.css의 색상 토큰 사용을 권장합니다.

♻️ 수정 제안 예시
-          <div className="text-h3 truncate text-[`#1E2C3F`]">{advertisement.title}</div>
-          <div className="flex shrink-0 items-center gap-0.5 rounded-full bg-[`#EAF6FB`] px-3 py-1.5 text-[10px] leading-3 font-semibold text-[`#2F6E83`]">
-            <CircleWarningIcon className="size-3 shrink-0" style={{ color: '#2F6E83' }} />
+          <div className="text-h3 truncate text-indigo-700">{advertisement.title}</div>
+          <div className="flex shrink-0 items-center gap-0.5 rounded-full bg-blue-50 px-3 py-1.5 text-[10px] leading-3 font-semibold text-blue-600">
+            <CircleWarningIcon className="size-3 shrink-0 text-blue-600" />

As per coding guidelines: "Prioritize color tokens from src/styles/theme.css"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Club/ClubList/components/AdvertisementCard.tsx` around lines 25 -
29, AdvertisementCard currently uses hardcoded color hexes (`#1E2C3F`, `#EAF6FB`,
`#2F6E83`) in the JSX (the title div and the badge div plus the inline style on
CircleWarningIcon); replace those hardcoded values with the corresponding theme
tokens defined in src/styles/theme.css (use the token variables instead of hex
literals), update the badge container classNames and the inline style on
CircleWarningIcon to reference the theme token (or a CSS class that reads the
token) and ensure advertisement.title rendering remains unchanged; target the
AdvertisementCard component and the elements rendering advertisement.title, the
badge div, and CircleWarningIcon when making the change.
src/apis/advertisement/index.ts (1)

1-2: import 경로를 path alias로 변경 필요

코딩 가이드라인에 따라 상대 경로 대신 @/* alias를 사용해야 합니다.

♻️ 수정 제안
-import { apiClient } from '../client';
-import type { AdvertisementsRequestParams, AdvertisementsResponse } from './entity';
+import { apiClient } from '@/apis/client';
+import type { AdvertisementsRequestParams, AdvertisementsResponse } from '@/apis/advertisement/entity';

As per coding guidelines: "Use path alias @/* for imports instead of relative paths"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/apis/advertisement/index.ts` around lines 1 - 2, Change the two relative
imports to use the project path alias: replace the import of apiClient
(currently from '../client') with the alias import from '@/apis/client' (or the
equivalent aliased module), and replace the type imports
AdvertisementsRequestParams and AdvertisementsResponse (currently from
'./entity') with the alias import from '@/apis/advertisement/entity'; update the
import statements for apiClient, AdvertisementsRequestParams, and
AdvertisementsResponse to use the '@/' path alias and ensure the module
specifiers match the TS/webpack path mappings in your project config.
src/utils/hooks/useAdvertisements.ts (2)

24-27: Mutation 에러 핸들링 고려

trackAdvertisementClick이 실패해도 사용자 경험에 영향이 없다면 현재 구조가 적절합니다. 다만 분석 데이터 누락을 모니터링하려면 onError 콜백 추가를 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/hooks/useAdvertisements.ts` around lines 24 - 27, Add an onError
handler to the useMutation that defines mutate: trackAdvertisementClick so
failures of postAdvertisementClick(advertisementId) are recorded; update the
useMutation call in useAdvertisements.ts to include an onError callback that
captures the advertisementId and error and sends them to your logging/monitoring
utility (or console) for analytics monitoring while keeping the
optimistic/no-UX-impact behavior.

16-22: 쿼리 에러 처리가 없습니다.

useQueries 결과에서 에러 상태를 처리하지 않고 있습니다. 광고 로딩 실패 시 사용자에게 피드백 없이 빈 배열만 반환됩니다.

광고 실패가 치명적이지 않다면 현재 구조도 괜찮지만, 최소한 로깅이나 모니터링을 위해 에러 상태 확인을 고려해주세요.

const hasError = advertisementQueries.some((query) => query.isError);
// 필요시 로깅 또는 fallback 처리
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/hooks/useAdvertisements.ts` around lines 16 - 22, The hook
currently ignores errors from useQueries; update the advertisementQueries
handling (the value returned from useQueries in useAdvertisements.ts) to detect
errors (e.g., advertisementQueries.some(q => q.isError)) and surface or log them
before returning results from getAdvertisements/ADVERTISEMENT_BATCH_SIZE
batches; specifically, check advertisementQueries for isError/isFetching states,
call your logger/monitoring (or return a fallback value) when any query.isError
is true, and ensure advertisementQueryKeys.randomBatch and getAdvertisements
remain the data sources while adding this error handling path.
src/contexts/useLayoutElementsContext.tsx (1)

8-8: Context 직접 export 검토

LayoutElementsContext가 직접 export되어 있습니다. Provider 설정을 위해 필요하지만, 코딩 가이드라인에서는 Context를 직접 노출하지 않도록 권장합니다.

Provider 전용 export를 분리하거나, 파일 내에서 Provider 컴포넌트를 함께 export하는 방식을 고려해보세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/contexts/useLayoutElementsContext.tsx` at line 8, Currently
LayoutElementsContext is exported directly; stop exposing the raw context and
instead export a Provider component (e.g., LayoutElementsProvider) and the
public hook (e.g., useLayoutElementsContext). Make LayoutElementsContext a
module-private constant (do not export it), implement and export a
LayoutElementsProvider that wraps children with that context, and export only
the provider and the hook so consumers cannot import the raw context object
(update any imports to use the new Provider/hook names).
src/utils/hooks/useAdvertisementInterval.ts (1)

101-101: itemCount dependency 의도 명확화

itemCount가 effect 내부에서 직접 사용되지 않지만 deps에 포함되어 있습니다. 아이템 수 변경 시 재측정을 트리거하려는 의도라면 주석으로 명시하면 좋겠습니다.

// itemCount: 아이템 수 변경 시 재측정 트리거
}, [bottomNavRef, enabled, firstItemRef, itemCount, mainRef, secondItemRef]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/hooks/useAdvertisementInterval.ts` at line 101, The dependency
array in useAdvertisementInterval includes itemCount even though it's not
referenced inside the effect; clarify intent by adding an inline comment next to
the deps array (or above the effect) that itemCount is included solely to
retrigger measurements when the number of items changes (e.g., "itemCount:
trigger re-measure when items change"), so future readers know this is
intentional; update the comment near the dependency array that currently lists
bottomNavRef, enabled, firstItemRef, itemCount, mainRef, secondItemRef to
document this behavior.
src/pages/Chat/index.tsx (1)

108-142: 외부 링크 접근성 개선 권장

광고 링크가 새 탭에서 열리는데, 스크린 리더 사용자를 위해 이를 명시하면 좋습니다.

♿ 접근성 개선 제안
     <a
       href={advertisement.linkUrl}
       target="_blank"
       rel="noopener noreferrer"
       onClick={() => onClick(advertisement.id)}
       className="active:bg-indigo-5 flex items-center gap-3 bg-white px-5 py-3 transition-colors"
+      aria-label={`${advertisement.title} (새 탭에서 열림)`}
     >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Chat/index.tsx` around lines 108 - 142, The anchor in
ChatAdvertisementListItem opens external links in a new tab but doesn't announce
that to assistive tech; update the <a> rendered by ChatAdvertisementListItem to
include an accessible indication (e.g., add an aria-label or title that appends
"opens in a new tab" to advertisement.title, or reference a visually-hidden
element via aria-describedby) so screen readers know the link opens in a new
tab; ensure the label references advertisement.title and advertisement.linkUrl
(or advertisement.id) so it remains unique and meaningful, and keep existing
target, rel and onClick behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/apis/advertisement/index.ts`:
- Around line 1-2: Change the two relative imports to use the project path
alias: replace the import of apiClient (currently from '../client') with the
alias import from '@/apis/client' (or the equivalent aliased module), and
replace the type imports AdvertisementsRequestParams and AdvertisementsResponse
(currently from './entity') with the alias import from
'@/apis/advertisement/entity'; update the import statements for apiClient,
AdvertisementsRequestParams, and AdvertisementsResponse to use the '@/' path
alias and ensure the module specifiers match the TS/webpack path mappings in
your project config.

In `@src/contexts/useLayoutElementsContext.tsx`:
- Line 8: Currently LayoutElementsContext is exported directly; stop exposing
the raw context and instead export a Provider component (e.g.,
LayoutElementsProvider) and the public hook (e.g., useLayoutElementsContext).
Make LayoutElementsContext a module-private constant (do not export it),
implement and export a LayoutElementsProvider that wraps children with that
context, and export only the provider and the hook so consumers cannot import
the raw context object (update any imports to use the new Provider/hook names).

In `@src/pages/Chat/index.tsx`:
- Around line 108-142: The anchor in ChatAdvertisementListItem opens external
links in a new tab but doesn't announce that to assistive tech; update the <a>
rendered by ChatAdvertisementListItem to include an accessible indication (e.g.,
add an aria-label or title that appends "opens in a new tab" to
advertisement.title, or reference a visually-hidden element via
aria-describedby) so screen readers know the link opens in a new tab; ensure the
label references advertisement.title and advertisement.linkUrl (or
advertisement.id) so it remains unique and meaningful, and keep existing target,
rel and onClick behavior.

In `@src/pages/Club/ClubList/components/AdvertisementCard.tsx`:
- Around line 25-29: AdvertisementCard currently uses hardcoded color hexes
(`#1E2C3F`, `#EAF6FB`, `#2F6E83`) in the JSX (the title div and the badge div plus the
inline style on CircleWarningIcon); replace those hardcoded values with the
corresponding theme tokens defined in src/styles/theme.css (use the token
variables instead of hex literals), update the badge container classNames and
the inline style on CircleWarningIcon to reference the theme token (or a CSS
class that reads the token) and ensure advertisement.title rendering remains
unchanged; target the AdvertisementCard component and the elements rendering
advertisement.title, the badge div, and CircleWarningIcon when making the
change.

In `@src/utils/hooks/useAdvertisementInterval.ts`:
- Line 101: The dependency array in useAdvertisementInterval includes itemCount
even though it's not referenced inside the effect; clarify intent by adding an
inline comment next to the deps array (or above the effect) that itemCount is
included solely to retrigger measurements when the number of items changes
(e.g., "itemCount: trigger re-measure when items change"), so future readers
know this is intentional; update the comment near the dependency array that
currently lists bottomNavRef, enabled, firstItemRef, itemCount, mainRef,
secondItemRef to document this behavior.

In `@src/utils/hooks/useAdvertisements.ts`:
- Around line 24-27: Add an onError handler to the useMutation that defines
mutate: trackAdvertisementClick so failures of
postAdvertisementClick(advertisementId) are recorded; update the useMutation
call in useAdvertisements.ts to include an onError callback that captures the
advertisementId and error and sends them to your logging/monitoring utility (or
console) for analytics monitoring while keeping the optimistic/no-UX-impact
behavior.
- Around line 16-22: The hook currently ignores errors from useQueries; update
the advertisementQueries handling (the value returned from useQueries in
useAdvertisements.ts) to detect errors (e.g., advertisementQueries.some(q =>
q.isError)) and surface or log them before returning results from
getAdvertisements/ADVERTISEMENT_BATCH_SIZE batches; specifically, check
advertisementQueries for isError/isFetching states, call your logger/monitoring
(or return a fallback value) when any query.isError is true, and ensure
advertisementQueryKeys.randomBatch and getAdvertisements remain the data sources
while adding this error handling path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 95c9e57a-b255-4cf9-83c8-8d055be6a781

📥 Commits

Reviewing files that changed from the base of the PR and between ce04c0f and e11ddfe.

📒 Files selected for processing (12)
  • src/apis/advertisement/entity.ts
  • src/apis/advertisement/index.ts
  • src/apis/advertisement/queries.ts
  • src/components/layout/BottomNav/index.tsx
  • src/components/layout/index.tsx
  • src/contexts/useLayoutElementsContext.tsx
  • src/pages/Chat/index.tsx
  • src/pages/Club/ClubList/components/AdvertisementCard.tsx
  • src/pages/Club/ClubList/components/ClubCard.tsx
  • src/pages/Club/ClubList/index.tsx
  • src/utils/hooks/useAdvertisementInterval.ts
  • src/utils/hooks/useAdvertisements.ts

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

광고 배너(목록 내 삽입형)를 동아리 목록/채팅 목록 화면에 노출하고, 클릭 트래킹 및 레이아웃 기반 노출 간격 계산을 위한 공통 훅/컨텍스트를 추가합니다.

Changes:

  • 광고 조회/클릭 트래킹을 위한 API 및 react-query 기반 훅(useAdvertisements) 추가
  • 스크롤 컨테이너/하단 네비게이션을 기준으로 광고 삽입 간격을 측정하는 훅(useAdvertisementInterval) 및 Layout ref 컨텍스트 추가
  • ClubList/Chat 리스트 렌더링에 광고 아이템을 주기적으로 삽입

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/utils/hooks/useAdvertisements.ts 광고 배치 조회(useQueries) 및 클릭 트래킹(useMutation) 훅 추가
src/utils/hooks/useAdvertisementInterval.ts 뷰포트/레이아웃 기반으로 “N개마다 광고”의 N 값을 측정하는 훅 추가
src/pages/Club/ClubList/index.tsx 동아리 카드 목록 사이에 광고 카드 삽입 로직 추가
src/pages/Club/ClubList/components/ClubCard.tsx 첫/둘째 아이템 측정을 위해 ref 전달 지원
src/pages/Club/ClubList/components/AdvertisementCard.tsx 동아리 리스트용 광고 카드 UI 추가
src/pages/Chat/index.tsx 채팅방 리스트 사이에 광고 아이템 삽입 및 광고 아이템 UI 추가
src/contexts/useLayoutElementsContext.tsx Layout의 main/bottomNav DOM ref를 제공하는 컨텍스트/훅 추가
src/components/layout/index.tsx main/bottomNav ref를 생성해 컨텍스트로 제공, BottomNav에 ref 전달
src/components/layout/BottomNav/index.tsx BottomNav root <nav>에 ref를 받을 수 있도록 props 추가
src/apis/advertisement/queries.ts 광고 react-query queryKey 정의 추가
src/apis/advertisement/index.ts 광고 조회/클릭 트래킹 API 추가
src/apis/advertisement/entity.ts 광고 엔티티 및 API 요청/응답 타입 추가

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

mainRef,
bottomNavRef,
}),
[]
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

useMemo is created with an empty dependency array but closes over mainRef/bottomNavRef. While these refs are stable today, including them in the dependency list (or avoiding useMemo and using a stable object pattern) prevents hook-deps lint noise and makes the memoization intent explicit.

Suggested change
[]
[mainRef, bottomNavRef]

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +21
const batchCount = Math.ceil(advertisementCount / ADVERTISEMENT_BATCH_SIZE);

const advertisementQueries = useQueries({
queries: Array.from({ length: batchCount }, (_, batchIndex) => ({
queryKey: advertisementQueryKeys.randomBatch(scope, batchIndex),
queryFn: () => getAdvertisements({ count: ADVERTISEMENT_BATCH_SIZE }),
staleTime: Infinity,
})),
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

useQueries here triggers 1 network request per batch (2 ads per request). For larger lists this becomes many parallel requests (e.g., 25 ads => 13 requests), which is unnecessary given getAdvertisements already supports a count param. Consider switching to a single query that requests count: advertisementCount (or at least reducing the number of requests by increasing the batch size / batching server-side).

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +21
queries: Array.from({ length: batchCount }, (_, batchIndex) => ({
queryKey: advertisementQueryKeys.randomBatch(scope, batchIndex),
queryFn: () => getAdvertisements({ count: ADVERTISEMENT_BATCH_SIZE }),
staleTime: Infinity,
})),
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The query always requests ADVERTISEMENT_BATCH_SIZE items, even for the final batch where fewer ads may be needed (e.g., advertisementCount is odd). Consider computing a per-batch count based on the remaining needed items to avoid fetching extra ads.

Suggested change
queries: Array.from({ length: batchCount }, (_, batchIndex) => ({
queryKey: advertisementQueryKeys.randomBatch(scope, batchIndex),
queryFn: () => getAdvertisements({ count: ADVERTISEMENT_BATCH_SIZE }),
staleTime: Infinity,
})),
queries: Array.from({ length: batchCount }, (_, batchIndex) => {
const remaining = advertisementCount - batchIndex * ADVERTISEMENT_BATCH_SIZE;
const count = Math.min(ADVERTISEMENT_BATCH_SIZE, remaining);
return {
queryKey: advertisementQueryKeys.randomBatch(scope, batchIndex),
queryFn: () => getAdvertisements({ count }),
staleTime: Infinity,
};
}),

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +12
function AdvertisementCard({ advertisement, onClick }: AdvertisementCardProps) {
return (
<a
href={advertisement.linkUrl}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

advertisement.linkUrl comes from the API and is used directly as an href. If an unexpected scheme like javascript: is returned, this can become an XSS/open-redirect vector. Consider validating/normalizing the URL (e.g., allow only http:/https:) before rendering it, and falling back to a safe value when invalid.

Suggested change
function AdvertisementCard({ advertisement, onClick }: AdvertisementCardProps) {
return (
<a
href={advertisement.linkUrl}
function getSafeHref(rawUrl: string | null | undefined): string {
if (!rawUrl) {
return '#';
}
try {
const base =
typeof window !== 'undefined' && window.location ? window.location.origin : 'http://localhost';
const url = new URL(rawUrl, base);
if (url.protocol === 'http:' || url.protocol === 'https:') {
// Preserve the original value to avoid unexpected normalization changes.
return rawUrl;
}
} catch {
// Ignore parse errors and fall through to safe default.
}
return '#';
}
function AdvertisementCard({ advertisement, onClick }: AdvertisementCardProps) {
const safeHref = getSafeHref(advertisement.linkUrl as unknown as string);
return (
<a
href={safeHref}

Copilot uses AI. Check for mistakes.
Comment thread src/pages/Chat/index.tsx
Comment on lines +109 to +114
return (
<a
href={advertisement.linkUrl}
target="_blank"
rel="noopener noreferrer"
onClick={() => onClick(advertisement.id)}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

advertisement.linkUrl is used directly as the anchor href. Since this value is server-provided, it should be validated/normalized (e.g., restrict to http:/https:) to prevent javascript: URLs or other unsafe schemes from being rendered.

Suggested change
return (
<a
href={advertisement.linkUrl}
target="_blank"
rel="noopener noreferrer"
onClick={() => onClick(advertisement.id)}
const getSafeHref = (url: string): string => {
try {
const parsed = new URL(url, window.location.origin);
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
return parsed.toString();
}
} catch {
// ignore parsing errors and treat as unsafe
}
return '';
};
const safeHref = getSafeHref(advertisement.linkUrl);
const handleClick = () => {
if (!safeHref) {
return;
}
onClick(advertisement.id);
};
return (
<a
href={safeHref || undefined}
target="_blank"
rel="noopener noreferrer"
onClick={handleClick}

Copilot uses AI. Check for mistakes.
@ff1451 ff1451 merged commit 362873e into develop Mar 20, 2026
6 checks passed
ff1451 added a commit that referenced this pull request Mar 20, 2026
ff1451 added a commit that referenced this pull request Mar 23, 2026
ff1451 added a commit that referenced this pull request Apr 1, 2026
* 205 feat 이미지 전처리 기능 구현 (#206)

* feat: 전처리 로직 및 WebWorker 구현

* feat: 전처리 적용 및 preview 동시성 제어 로직 추가

* refactor: 리뷰 반영

* [hotfix] 하단바 너비 수정 (#208)

* hotfix: 하단바 너비 수정

* chore: 불필요한 값 제거

* refactor: 고정 gap 제거

* Reapply "[feat] 광고 배너 추가 (#200)"

This reverts commit c51ec85.

* [feat] 하단바 리디자인 (#213)

* chore: asset 추가

* feat: 하단바 리디자인 반영 및 레이아웃 수정

* [refactor] 광고 카드 레이아웃 밀림 수정 (#215)

* refactor: 광고 개수 측정 시기 변경 및 기본값 제거

* feat: 스켈레톤 UI 추가

* feat: 인앱 알림 페이지 및 토스트 구현 (#217)

* feat: 알림 API 및 스트림 기반 추가

* feat: 인앱 알림 레이어 추가

* feat: 알림 페이지 및 헤더 진입 구현

* fix: 알림 스트림 401 재시도 조건 정리

* fix: 알림 목록 이동 차단 제거

* refactor: 알림 공용 훅 위치 정리

* fix: 알림 재연결 캐시 동기화 추가

* fix: 알림 목록 토스트 큐 누적 방지

* fix: 알림 읽음 카운트 감소 조건 보강

* [refactor] 도메인별 TanStack Query 훅 정리 (#219)

* chore: pwa용 이미지 제거

* refactor: auth 도메인 쿼리와 뮤테이션 정리

* refactor: council과 schedule 조회 훅 정리

* refactor: chat과 notification 캐시 처리 정리

* refactor: club 조회와 지원 플로우 정리

* refactor: manager 도메인 캐시 처리 정리

* refactor: studyTime 도메인 쿼리와 뮤테이션 정리

* refactor: 광고와 업로드 도메인 훅 정리

* [refactor] mutaton query 및 hook 추가 수정 (#221)

* refactor: auth와 user myInfo 훅 정리

* refactor: club과 schedule 조회 훅 정리

* refactor: chat과 notification 훅 구조 정리

* refactor: club 지원 뮤테이션 훅 정리

* refactor: manager 뮤테이션 훅 구조 정리

* refactor: mutation 훅 cache 정리

* refactor: 컨벤션 통일

* refactor: isRead 조건 정리

* fix: 채팅 스크롤 문제 수정

* refactor: 불필요한 코드 제거

* [fix] 모바일 환경 입력창과 키보드 간의 간격이 큰 문제 수정 (#223)

* chore: 가공용 safeArea 변수 선언

* refactor: 고정 패딩 값 수정 및 safeArea 적용 변경

* feat: 키보드 활성화 감지 및 safeArea 적용 여부 기능 추가

* refactor: 매직넘버 상수화 및 가로모드 처리

* [fix] 키보드 활성화 시 화면 흔들림 문제 수정 (#225)

* refactor: 채팅 viewport 훅 네이밍 정리

* refactor: viewport 높이 잠금 훅 적용 시점 조정

* [fix] 키보드 활성화 시 채팅 화면 전체가 흔들리는 문제 수정 (#227)

* refactor: 채팅 viewport 훅 네이밍 정리

* refactor: viewport 높이 잠금 훅 적용 시점 조정

* fix: 채팅 화면 스크롤 잠금으로 키보드 흔들림 완화

* fix: 입력 포커스 중 viewport offset 고정 (#229)

* fix: 문서 루트 스크롤 잠금으로 빈 공간 잔류 방지 (#232)

* [fix] 키보드 활성화 시 채팅 화면 상단 고정이 깨지고 빈 공간이 남는 문제 수정 (#234)

* fix: 채팅 화면 상단 고정 깨짐과 빈 공간 잔류 수정

* refactor: 라우트 조건 수정

* fix: 문서 스크롤 위치 감지 보강

* refactor: 입력 요소 판별 유틸과 스크롤 주석 정리

* [fix] 키보드 활성화 시 채팅 화면에서 문서 스크롤이 발생하는 문제 수정 (#236)

* fix: 채팅 문서 스크롤 제스처 차단

* fix: 입력 요소 터치 동작 예외 처리

* [fix] 키보드 활성화 시 채팅방이 마지막 메시지 위치를 유지하지 못하는 문제 수정 (#238)

* fix: 키보드 활성화 시 채팅 하단 정렬 유지

* refactor: 채팅 리사이즈 관찰 안정화

* fix: mypage 연계 약관 페이지 뒤로가기 수정 (#240)

* refactor: alias import 경로 정리

* fix: query 설정과 suspense 분기 정리

* refactor: 관리자 화면 스타일 유틸 정리

* fix: 이미지 전처리 예외 처리 보강

* fix: 헤더와 회비 화면 동작 정리

* fix: 공통 유틸 안정성 개선

* fix: 이미지 전처리 실패 처리를 보정

* fix: 모집 공고 저장 후 설정 반영 순서 조정

* fix: 부원 직책 변경 실패 처리를 보강

* fix: 약관 링크 접근성을 개선

* fix: 공통 쿼리와 유틸 안정성을 보완

* [feat] 동적 버전 정보 표시 구현 (#211)

* feat: 동적 버전 정보 표시 구현

* refactor: 버전 정보 미 존재시 v 표시 제거

* [feat] 메인화면 동아리 카드 디자인 수정 반영 (#242)

* feat: 메인화면 동아리 카드 디자인 수정

* chore: 하단바 아이콘 수정

* refactor: 코드래빗 리뷰 반영

* refactor: and 연산자로 변경

* apiClient 코드 중복 제거 및 네이티브 브릿지 인증 동기화 중앙화 (#244)

* refactor: apiClient 코드 중복 제거 및 네이티브 브릿지 인증 동기화 중앙화

* refactor: body 직렬화 가드를 plain object/array로 한정

* fix: body 읽기 중 AbortError가 ParseError로 오분류되는 문제 수정

* [refactor] 에러 처리 유틸 및 utils 구조 정리 (#246)

* refactor: 에러 처리 유틸 및 공통 토스트 흐름 정리

* refactor: utils 폴더 구조를 역할별로 정리

* refactor: 코드래빗 리뷰 반영

* refactor: 코드래빗 리뷰 반영

* Update src/pages/Home/components/HomeClubSection.tsx

* fix: 인증 세션 복구 흐름 정리

* fix: 홈 동아리 카드 레이아웃 정리

* [feat] 총동아리 페이지 리디자인 및 하단 오버레이 정리 (#249)

* refactor: 하단 오버레이 처리 공통화

* feat: 총동아리 페이지와 헤더 리디자인 반영

* fix: 채팅 하단 여백과 외부 링크 속성 수정

* refactor: 총동아리 헤더 설정 정리

* fix: 총동아리 상세 접근성과 스타일 보완

* [feat] 마이페이지 관리자 카드 분리 및 채팅 미확인 배지 반영 (#251)

* feat: 하단 채팅 배지 표시 및 조회 조건 보완

* refactor: 관리자 정보 카드 컴포넌트 분리

* feat: 채팅 페이지 리디자인 (#252)

* feat: 채팅 페이지 리디자인

* fix:tailwind 문법 수정

* fix: 코드 수정

* fix: 폰트 색상 및 위치 수정

* fix: 채팅방 사람수 정렬

* fix: 오타 수정

* chore: conflict 해결 중 누락된 부분 수정

* [refactor] 광고 렌더링 조건 수정 (#254)

* refactor: 광고 렌더링 조건 수정

* docs: 문서명 변경

---------

Co-authored-by: 박성주 <145267904+ParkSungju01@users.noreply.github.com>
@ff1451 ff1451 deleted the 195-feat-광고-배너-추가 branch April 7, 2026 09:33
ff1451 added a commit that referenced this pull request Apr 21, 2026
* [배포] 이미지 전처리, 광고 기능, 하단바 리디자인, 인앱 알림 페이지 및 토스트 프로덕션 배포 (#230)

* 205 feat 이미지 전처리 기능 구현 (#206)

* feat: 전처리 로직 및 WebWorker 구현

* feat: 전처리 적용 및 preview 동시성 제어 로직 추가

* refactor: 리뷰 반영

* [hotfix] 하단바 너비 수정 (#208)

* hotfix: 하단바 너비 수정

* chore: 불필요한 값 제거

* refactor: 고정 gap 제거

* Reapply "[feat] 광고 배너 추가 (#200)"

This reverts commit c51ec85.

* [feat] 하단바 리디자인 (#213)

* chore: asset 추가

* feat: 하단바 리디자인 반영 및 레이아웃 수정

* [refactor] 광고 카드 레이아웃 밀림 수정 (#215)

* refactor: 광고 개수 측정 시기 변경 및 기본값 제거

* feat: 스켈레톤 UI 추가

* feat: 인앱 알림 페이지 및 토스트 구현 (#217)

* feat: 알림 API 및 스트림 기반 추가

* feat: 인앱 알림 레이어 추가

* feat: 알림 페이지 및 헤더 진입 구현

* fix: 알림 스트림 401 재시도 조건 정리

* fix: 알림 목록 이동 차단 제거

* refactor: 알림 공용 훅 위치 정리

* fix: 알림 재연결 캐시 동기화 추가

* fix: 알림 목록 토스트 큐 누적 방지

* fix: 알림 읽음 카운트 감소 조건 보강

* [refactor] 도메인별 TanStack Query 훅 정리 (#219)

* chore: pwa용 이미지 제거

* refactor: auth 도메인 쿼리와 뮤테이션 정리

* refactor: council과 schedule 조회 훅 정리

* refactor: chat과 notification 캐시 처리 정리

* refactor: club 조회와 지원 플로우 정리

* refactor: manager 도메인 캐시 처리 정리

* refactor: studyTime 도메인 쿼리와 뮤테이션 정리

* refactor: 광고와 업로드 도메인 훅 정리

* [refactor] mutaton query 및 hook 추가 수정 (#221)

* refactor: auth와 user myInfo 훅 정리

* refactor: club과 schedule 조회 훅 정리

* refactor: chat과 notification 훅 구조 정리

* refactor: club 지원 뮤테이션 훅 정리

* refactor: manager 뮤테이션 훅 구조 정리

* refactor: mutation 훅 cache 정리

* refactor: 컨벤션 통일

* refactor: isRead 조건 정리

* fix: 채팅 스크롤 문제 수정

* refactor: 불필요한 코드 제거

* [fix] 모바일 환경 입력창과 키보드 간의 간격이 큰 문제 수정 (#223)

* chore: 가공용 safeArea 변수 선언

* refactor: 고정 패딩 값 수정 및 safeArea 적용 변경

* feat: 키보드 활성화 감지 및 safeArea 적용 여부 기능 추가

* refactor: 매직넘버 상수화 및 가로모드 처리

* [fix] 키보드 활성화 시 화면 흔들림 문제 수정 (#225)

* refactor: 채팅 viewport 훅 네이밍 정리

* refactor: viewport 높이 잠금 훅 적용 시점 조정

* [fix] 키보드 활성화 시 채팅 화면 전체가 흔들리는 문제 수정 (#227)

* refactor: 채팅 viewport 훅 네이밍 정리

* refactor: viewport 높이 잠금 훅 적용 시점 조정

* fix: 채팅 화면 스크롤 잠금으로 키보드 흔들림 완화

* fix: 입력 포커스 중 viewport offset 고정 (#229)

* fix: 문서 루트 스크롤 잠금으로 빈 공간 잔류 방지 (#232)

* [fix] 키보드 활성화 시 채팅 화면 상단 고정이 깨지고 빈 공간이 남는 문제 수정 (#234)

* fix: 채팅 화면 상단 고정 깨짐과 빈 공간 잔류 수정

* refactor: 라우트 조건 수정

* fix: 문서 스크롤 위치 감지 보강

* refactor: 입력 요소 판별 유틸과 스크롤 주석 정리

* [fix] 키보드 활성화 시 채팅 화면에서 문서 스크롤이 발생하는 문제 수정 (#236)

* fix: 채팅 문서 스크롤 제스처 차단

* fix: 입력 요소 터치 동작 예외 처리

* [fix] 키보드 활성화 시 채팅방이 마지막 메시지 위치를 유지하지 못하는 문제 수정 (#238)

* fix: 키보드 활성화 시 채팅 하단 정렬 유지

* refactor: 채팅 리사이즈 관찰 안정화

* fix: mypage 연계 약관 페이지 뒤로가기 수정 (#240)

* refactor: alias import 경로 정리

* fix: query 설정과 suspense 분기 정리

* refactor: 관리자 화면 스타일 유틸 정리

* fix: 이미지 전처리 예외 처리 보강

* fix: 헤더와 회비 화면 동작 정리

* fix: 공통 유틸 안정성 개선

* fix: 이미지 전처리 실패 처리를 보정

* fix: 모집 공고 저장 후 설정 반영 순서 조정

* fix: 부원 직책 변경 실패 처리를 보강

* fix: 약관 링크 접근성을 개선

* fix: 공통 쿼리와 유틸 안정성을 보완

* [feat] 동적 버전 정보 표시 구현 (#211)

* feat: 동적 버전 정보 표시 구현

* refactor: 버전 정보 미 존재시 v 표시 제거

* [feat] 메인화면 동아리 카드 디자인 수정 반영 (#242)

* feat: 메인화면 동아리 카드 디자인 수정

* chore: 하단바 아이콘 수정

* refactor: 코드래빗 리뷰 반영

* refactor: and 연산자로 변경

* apiClient 코드 중복 제거 및 네이티브 브릿지 인증 동기화 중앙화 (#244)

* refactor: apiClient 코드 중복 제거 및 네이티브 브릿지 인증 동기화 중앙화

* refactor: body 직렬화 가드를 plain object/array로 한정

* fix: body 읽기 중 AbortError가 ParseError로 오분류되는 문제 수정

* [refactor] 에러 처리 유틸 및 utils 구조 정리 (#246)

* refactor: 에러 처리 유틸 및 공통 토스트 흐름 정리

* refactor: utils 폴더 구조를 역할별로 정리

* refactor: 코드래빗 리뷰 반영

* refactor: 코드래빗 리뷰 반영

* Update src/pages/Home/components/HomeClubSection.tsx

* fix: 인증 세션 복구 흐름 정리

* fix: 홈 동아리 카드 레이아웃 정리

* [feat] 총동아리 페이지 리디자인 및 하단 오버레이 정리 (#249)

* refactor: 하단 오버레이 처리 공통화

* feat: 총동아리 페이지와 헤더 리디자인 반영

* fix: 채팅 하단 여백과 외부 링크 속성 수정

* refactor: 총동아리 헤더 설정 정리

* fix: 총동아리 상세 접근성과 스타일 보완

* [feat] 마이페이지 관리자 카드 분리 및 채팅 미확인 배지 반영 (#251)

* feat: 하단 채팅 배지 표시 및 조회 조건 보완

* refactor: 관리자 정보 카드 컴포넌트 분리

* feat: 채팅 페이지 리디자인 (#252)

* feat: 채팅 페이지 리디자인

* fix:tailwind 문법 수정

* fix: 코드 수정

* fix: 폰트 색상 및 위치 수정

* fix: 채팅방 사람수 정렬

* fix: 오타 수정

* chore: conflict 해결 중 누락된 부분 수정

* [refactor] 광고 렌더링 조건 수정 (#254)

* refactor: 광고 렌더링 조건 수정

* docs: 문서명 변경

---------

Co-authored-by: 박성주 <145267904+ParkSungju01@users.noreply.github.com>

* refactor: 가이드 페이지 이미지 변경 및 구조 개선 (#260)

* hotfix: 가이드 이미지 경로 변경

---------

Co-authored-by: 박성주 <145267904+ParkSungju01@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 광고 배너 추가

2 participants