Skip to content

Commit 211b186

Browse files
authored
Merge pull request #47 from SWYP-mingling/feature/GA-Evaluation-Metric
2 parents e96a305 + d7ce582 commit 211b186

3 files changed

Lines changed: 92 additions & 5 deletions

File tree

app/meeting/[id]/page.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { useToast } from '@/hooks/useToast';
1616
import Toast from '@/components/ui/toast';
1717
import { getMeetingUserId, removeMeetingUserId } from '@/lib/storage';
1818
import { sendGAEvent } from '@next/third-parties/google';
19-
2019
interface StationInfo {
2120
line: string;
2221
name: string;
@@ -170,6 +169,21 @@ export default function Page() {
170169
show();
171170
return;
172171
}
172+
173+
if (typeof window !== 'undefined') {
174+
const calculationType = id ? 'recalculated' : 'first';
175+
const isHost = localStorage.getItem(`is_host_${id}`) === 'true';
176+
const userRole = isHost ? 'host' : 'participant';
177+
const browserId = localStorage.getItem('browser_id');
178+
179+
sendGAEvent('event', 'midpoint_calculated', {
180+
meeting_url_id: id,
181+
browser_id: browserId,
182+
role: userRole,
183+
calculation_type: calculationType,
184+
});
185+
}
186+
173187
router.push(`/result/${id}`);
174188
};
175189

app/recommend/page.tsx

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Image from 'next/image';
66
import KakaoMapRecommend from '@/components/map/kakaoMapRecommend';
77
import { useRecommend } from '@/hooks/api/query/useRecommend';
88
import { useCheckMeeting } from '@/hooks/api/query/useCheckMeeting';
9+
import { sendGAEvent } from '@next/third-parties/google';
910

1011
function RecommendContent() {
1112
const router = useRouter();
@@ -139,15 +140,49 @@ function RecommendContent() {
139140
router.back();
140141
};
141142

142-
const handleOpenKakaoMap = (e: React.MouseEvent, placeUrl?: string) => {
143+
const handleOpenKakaoMap = (
144+
e: React.MouseEvent,
145+
placeUrl?: string,
146+
place?: (typeof places)[0]
147+
) => {
143148
e.stopPropagation();
149+
150+
// 카카오맵에서 보기 클릭 시 GA 전송 (external_map_opened)
151+
if (typeof window !== 'undefined' && meetingId && place) {
152+
const browserId = localStorage.getItem('browser_id');
153+
const isHost = localStorage.getItem(`is_host_${meetingId}`) === 'true';
154+
const userRole = isHost ? 'host' : 'participant';
155+
const candidateId = `place_${String(place.id).padStart(2, '0')}`;
156+
157+
sendGAEvent('event', 'external_map_opened', {
158+
meeting_url_id: meetingId,
159+
user_cookie_id: browserId,
160+
role: userRole,
161+
candidate_id: candidateId,
162+
});
163+
}
164+
144165
if (placeUrl) {
145166
window.open(placeUrl, '_blank', 'noopener,noreferrer');
146167
} else {
147168
window.open('https://map.kakao.com', '_blank', 'noopener,noreferrer');
148169
}
149170
};
150171

172+
// 장소 리스트 중 하나 클릭 시 GA 전송 (place_list_viewed)
173+
const handlePlaceClick = (place: (typeof places)[0]) => {
174+
setSelectedPlaceId(place.id);
175+
if (meetingId) {
176+
const candidateId = `place_${String(place.id).padStart(2, '0')}`;
177+
sendGAEvent('event', 'place_list_viewed', {
178+
meeting_url_id: meetingId,
179+
candidate_id: candidateId,
180+
place_category: place.category,
181+
rank_order: place.id,
182+
});
183+
}
184+
};
185+
151186
return (
152187
<div className="flex items-center justify-center p-0 md:min-h-[calc(100vh-200px)] md:py-25">
153188
<div className="flex h-full w-full flex-col bg-white md:h-175 md:w-174 md:flex-row md:gap-2 lg:w-215">
@@ -194,7 +229,7 @@ function RecommendContent() {
194229
{places.map((place) => (
195230
<div
196231
key={place.id}
197-
onClick={() => setSelectedPlaceId(place.id)}
232+
onClick={() => handlePlaceClick(place)}
198233
className={`flex cursor-pointer flex-col gap-2 rounded border p-4 ${
199234
selectedPlaceId === place.id
200235
? 'border-blue-5 border-2'
@@ -247,7 +282,7 @@ function RecommendContent() {
247282
{/* 하단 버튼은 조건부 렌더링 */}
248283
{selectedPlaceId === place.id ? (
249284
<button
250-
onClick={(e) => handleOpenKakaoMap(e, place.placeUrl)}
285+
onClick={(e) => handleOpenKakaoMap(e, place.placeUrl, place)}
251286
className="bg-gray-8 w-full rounded py-2 text-[15px] text-white"
252287
>
253288
카카오맵에서 보기

app/result/[id]/page.tsx

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,37 @@ export default function Page() {
149149

150150
const [selectedResultId, setSelectedResultId] = useState<number>(1);
151151

152+
// 중간지점 후보 조회 GA 이벤트
153+
const trackMidpointCandidateViewed = useCallback(
154+
(candidateRankOrder: number, candidateId: string) => {
155+
if (typeof window === 'undefined' || !id) return;
156+
const browserId = localStorage.getItem('browser_id');
157+
const isHost = localStorage.getItem(`is_host_${id}`) === 'true';
158+
const userRole = isHost ? 'host' : 'participant';
159+
160+
sendGAEvent('event', 'midpoint_candidate_viewed', {
161+
meeting_url_id: id,
162+
user_cookie_id: browserId,
163+
role: userRole,
164+
candidate_rank_order: candidateRankOrder,
165+
candidate_id: candidateId,
166+
});
167+
},
168+
[id]
169+
);
170+
171+
// 장소 리스트에서 결과보기 페이지로 돌아왔을 때 midpoint_candidate_viewed 전송
172+
useEffect(() => {
173+
if (typeof window === 'undefined' || !id || locationResults.length === 0) return;
174+
const fromRecommend = sessionStorage.getItem(`from_recommend_${id}`);
175+
if (fromRecommend !== '1') return;
176+
177+
sessionStorage.removeItem(`from_recommend_${id}`);
178+
const selected = locationResults.find((r) => r.id === selectedResultId) ?? locationResults[0];
179+
const candidateId = `mid_${selected.endStation.replace(/\s+/g, '_')}`;
180+
trackMidpointCandidateViewed(selected.id, candidateId);
181+
}, [id, locationResults, selectedResultId, trackMidpointCandidateViewed]);
182+
152183
// 뒤로 가기 클릭 시 캐시 데이터 무효화
153184
const clearRelatedCache = useCallback(() => {
154185
queryClient.removeQueries({ queryKey: ['midpoint', id] });
@@ -280,13 +311,20 @@ export default function Page() {
280311
if (meetingType) params.append('meetingType', meetingType);
281312
if (categoryParam) params.append('category', categoryParam);
282313

314+
if (typeof window !== 'undefined') {
315+
sessionStorage.setItem(`from_recommend_${id}`, '1');
316+
}
283317
router.push(`/recommend?${params.toString()}`);
284318
};
285319

286320
return (
287321
<div
288322
key={result.id}
289-
onClick={() => setSelectedResultId(result.id)}
323+
onClick={() => {
324+
setSelectedResultId(result.id);
325+
const candidateId = `mid_${result.endStation.replace(/\s+/g, '_')}`;
326+
trackMidpointCandidateViewed(result.id, candidateId);
327+
}}
290328
className={`flex cursor-pointer flex-col gap-3.75 rounded border bg-white p-5 ${
291329
selectedResultId === result.id
292330
? 'border-blue-5 border-2'

0 commit comments

Comments
 (0)