analyze_commercial_area
Analyze commercial areas for startups by evaluating business density, saturation levels, and area characteristics based on location and business type.
Instructions
특정 위치의 상권을 분석합니다. 업종별 밀집도, 포화도, 상권 특성을 제공합니다. 카카오맵 데이터를 기반으로 합니다.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| location | Yes | 분석할 위치 (예: 강남역, 홍대입구, 부산 서면) | |
| business_type | Yes | 창업 예정 업종 (예: 카페, 음식점, 편의점, 미용실) | |
| radius | No | 분석 반경 (m), 기본값: 500 |
Implementation Reference
- src/tools/commercial-area.ts:91-169 (handler)The core handler function `analyzeCommercialArea` that performs commercial area analysis: gets coordinates, counts categories and competitors via Kakao API, computes metrics like saturation score, area type, characteristics, and recommendation.export async function analyzeCommercialArea( location: string, businessType: string, radius: number = 500 ): Promise<ApiResult<CommercialAreaData>> { try { // 1. 위치 좌표 얻기 const coords = await kakaoApi.getCoordinates(location); if (!coords) { return { success: false, error: { code: "LOCATION_NOT_FOUND", message: `입력하신 위치를 찾을 수 없습니다: ${location}`, suggestion: "강남역, 홍대입구 등 구체적인 지명을 입력해주세요.", }, }; } // 2. 업종별 업체 수 조회 const categoryBreakdown = await kakaoApi.countByCategories( String(coords.lng), String(coords.lat), radius ); // 3. 해당 업종 업체 검색 const competitors = await kakaoApi.findCompetitors( businessType, location, radius, 15 ); const sameCategoryCount = competitors.length; // 4. 분석 결과 계산 const totalStores = Object.values(categoryBreakdown).reduce((a, b) => a + b, 0); const saturationScore = calculateSaturationScore(businessType, sameCategoryCount, totalStores); const areaType = estimateAreaType(categoryBreakdown); const characteristics = analyzeCharacteristics(categoryBreakdown, location); const recommendation = generateRecommendation(businessType, saturationScore, sameCategoryCount); return { success: true, data: { location: { name: location, address: location, coordinates: coords, }, areaType, characteristics, density: { totalStores, categoryBreakdown, sameCategoryCount, saturationLevel: getSaturationLevel(saturationScore), saturationScore, }, recommendation, }, meta: { source: DATA_SOURCES.kakaoLocal, timestamp: new Date().toISOString(), }, }; } catch (error) { console.error("상권 분석 실패:", error); return { success: false, error: { code: "ANALYSIS_FAILED", message: `상권 분석 중 오류가 발생했습니다: ${error instanceof Error ? error.message : "Unknown error"}`, suggestion: "잠시 후 다시 시도해주세요.", }, }; } }
- src/index.ts:24-39 (registration)MCP server registration of the 'analyze_commercial_area' tool, including description, Zod input schema, and wrapper handler that calls the core function and formats response.server.tool( "analyze_commercial_area", "특정 위치의 상권을 분석합니다. 업종별 밀집도, 포화도, 상권 특성을 제공합니다. 카카오맵 데이터를 기반으로 합니다.", { location: z.string().describe("분석할 위치 (예: 강남역, 홍대입구, 부산 서면)"), business_type: z.string().describe("창업 예정 업종 (예: 카페, 음식점, 편의점, 미용실)"), radius: z.number().optional().default(500).describe("분석 반경 (m), 기본값: 500"), }, async ({ location, business_type, radius }) => { const result = await analyzeCommercialArea(location, business_type, radius); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], isError: !result.success, }; } );
- src/types.ts:33-45 (schema)TypeScript interface defining the structured output data for the commercial area analysis tool.export interface CommercialAreaData { location: Location; areaType: string; characteristics: string[]; density: { totalStores: number; categoryBreakdown: Record<string, number>; sameCategoryCount: number; saturationLevel: string; saturationScore: number; }; recommendation: string; }
- src/tools/commercial-area.ts:71-89 (helper)Helper function to compute saturation score based on business type and competitor count relative to predefined optimal densities.function calculateSaturationScore( businessType: string, sameCategoryCount: number, _totalStores: number ): number { // 업종별 적정 개수 기준 (반경 500m 기준) const optimalCounts: Record<string, number> = { 카페: 10, 음식점: 20, 편의점: 5, 미용실: 8, default: 10, }; const optimal = optimalCounts[businessType] || optimalCounts.default; const ratio = (sameCategoryCount / optimal) * 100; return Math.min(100, Math.round(ratio)); }