1. 진행 순서
시나리오 → 아키텍처 → ERD → 샘플 데이터 검증 → PHP 앱(관리자, 사용자)
시나리오 : 사람이 하는 일을 "트리거-입력-규칙-출력-저장"으로 변경한다
아키텍처 : 액터/시스템/데이터 흐름을 한장으로 고정해 만든다
ERD : 데이터 구조(=앱이 할 일에 대한 뼈대)를 설계한다
화면설계 : 와이어프레임 수준의 화면 설계로 전체 흐름을 구체화하고 빠진 부분이 없는지 확인한다
샘플데이터 검증 : 설계가 시나리오를 실제로 만족하는지 SQL로 확인한다
관리자 → 사용자 → 자동화의 순서 : 운영(데이터 관리) 기반이 먼저 있어야 자동화도 안정적일 수 있다
2. 사용할 API
네이버 지도 API와 검색 API를 이용
- 지도 표시 (front) : 지도 JS SDK를 브라우저에서 로드해 지도/마커를 그림
- 장소 검색 (server) : 서버(PHP)에서 '지역 검색' API를 호출해 JSON 결과를 받음
- 좌표/주소 보강 : 주소 ↔ 좌표 변환 API(Geocoding/Reverse Geocoding)를 필요시 추가하는 방법
3. 준비
① 네이버 검색 API
아래 네이버 개발자센터 계정이 필요합니다
NAVER Developers
네이버 오픈 API들을 활용해 개발자들이 다양한 애플리케이션을 개발할 수 있도록 API 가이드와 SDK를 제공합니다. 제공중인 오픈 API에는 네이버 로그인, 검색, 단축URL, 캡차를 비롯 기계번역, 음
developers.naver.com
가입 후 진행
홈페이지 상단 Application - 애플리케이션 등록 클릭

* 선택
이름 : 구분할 수 있을 정도로만 설정
사용 API : 검색
환경추가 : WEB환경 추가
웹서비스 URL : http://localhost

등록하기 초록색 버튼을 누르면
Client ID
Client Secret
키값이 두개 나오는데 메모장에 복붙해놓으시면 됩니다

② 네이버 지도 API
1) 아래 사이트 가입이 필요합니다
NAVER CLOUD PLATFORM 네이버 클라우드 플랫폼
NAVER CLOUD PLATFORM
cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification
www.ncloud.com
여기에서 Maps api를 사용할 예정입니다

이 사이트는 많이 사용하면 과금이 되는데 결제 수단을 관리해야 해당 Maps api를 사용할 수 있습니다
2) 결제 수단 등록
웹페이지 상단 메뉴에서 마이페이지 > 결제수단 관리를 누르면 결제 정보 관리 를 입력할 수 있는 콘솔 창으로 이동
혹시나 하는 마음에 0원인 체크카드를 등록해놨습니다
3) API 사용
다시 Maps API로 이동합니다

이용 신청
4) 애플리케이션 등록
파란색 +Application 등록 버튼을 눌러 애플리케이션 등록을 진행


이름 : neighborhood-place
API 선택 : 전체 다
서비스 환경 등록 - Web 서비스 URL : http://localhost



Client ID를 따로 메모해놓으세요
③ 필요값 정리
☞ Naver Developers의 Client ID, Client Secret
☞ Naver CLOUD PLATFORM의 Client ID
4. 애플리케이션 요구사항 정리
① 어플리케이션 정보
우리동네 장소 찾기 + 즐겨찾기 + 리포트
② 관리자 기능 (필수)
▷ 추천 검색 키워드 관리(추가/수정/비활성)
▷ 추천 지역(기본 검색어 프리셋) 관리
③ 사용자 기능 (필수)
▷ 키워드(예 : '광교 카페')로 장소 검색
▷ 검색 결과를 지도에 마커로 표시
▷ 장소 상세(이름, 주소, 전화, 링크) 보기
▷ 즐겨찾기 추가/삭제
5. 서비스 시나리오
트리거 / 액터 / 입력 / 처리 규칙 / 출력 / DB 저장
ⓘ 시나리오 A
트리거 : 검색 버튼 클릭
액터 : 사용자
입력 : keyword(필수), display(선택, 기본 5)
규칙 : 공백/길이 체크, API 실패 시 재시도 1회
출력 : 장소 리스트 + 지도 마커
저장 : search_log 기록, place_cache 결과 insert
ⓙ 시나리오 B
트리거 : 즐겨찾기 추가 클릭
액터 : 사용자
입력 : place_id
규칙 : 중복 방지(unique), 로그인 필수
출력 : 저장 완료 메세지
저장 : favorite_place insert
6. 시스템 아키텍처 : 데이터 흐름 ASCII 다이어그램

7. 와이어프레임 : 모든 화면 ASCII 포맷
명확해야 하는 세 가지
1) 입력
2) 출력
3) 행동
7-1. 공통
① U-00. 공통 네비게이션 (모든 화면 상단)
+--------------------------------------------------------------+
| 우리동네 장소 찾기 |
| [홈] [검색] [즐겨찾기] [최근검색] [로그인/내정보] |
+--------------------------------------------------------------+
② U-01. 메인 화면
+--------------------------------------------------------------+
| (U-01) 홈 |
+--------------------------------------------------------------+
| 빠른 검색 |
| 키워드: [_________________________] (예: 광교 카페) [검색] |
| 표시개수: (5) (10) (20) |
| |
| 추천 키워드(관리자 설정) |
| - [광교 카페] - [광교 맛집] - [수원 공부하기 좋은 카페] |
| |
| 오늘 자동 리포트 요약(Top3) |
| [키워드] [1위 장소] [2위 장소] [3위 장소] |
+--------------------------------------------------------------+
| 안내: 검색 시 최근 검색 기록이 저장됩니다. |
+--------------------------------------------------------------+
③ U-02. 검색 + 지도(핵심 화면)
+--------------------------------------------------------------+
| (U-02) 장소 검색 |
+--------------------------------------------------------------+
| 키워드: [_________________________] [검색] |
| 표시개수: [ 5 ▼ ] 정렬: [정확도 ▼] |
| [ ] 검색 로그 저장(기본 ON) |
+---------------------------+----------------------------------+
| 결과 리스트(최대 N개) | 지도(Map) |
| 1. [장소명] | +------------------------------+ |
| [도로명/지번] | | | |
| [전화] [상세] [★] | | (지도) | |
| | | (마커들 표시) | |
| 2. [장소명] | | | |
| ... | +------------------------------+ |
+---------------------------+----------------------------------+
| 메시지 영역: (예) "검색 결과 5개를 표시했습니다." |
| 오류 예: "키워드를 입력해 주세요." / "API 호출 실패" |
+--------------------------------------------------------------+
④ U-03. 장소 상세
+--------------------------------------------------------------+
| (U-03) 장소 상세 |
+--------------------------------------------------------------+
| [장소명] [★ 즐겨찾기 추가/삭제] |
| 주소: [도로명 주소 / 지번 주소] |
| 전화: [000-0000-0000] |
| 링크: [원문 링크 열기] |
| 좌표: (lat, lng) / (mapx, mapy) |
+--------------------------------------------------------------+
| 지도 미리보기(마커 1개) |
| +----------------------------------------------------------+ |
| | | |
| | (지도) | |
| | | |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+
| [뒤로] |
+--------------------------------------------------------------+
⑤ U-04. 즐겨찾기 목록
+--------------------------------------------------------------+
| (U-04) 즐겨찾기 |
+--------------------------------------------------------------+
| 필터: [전체 ▼] 정렬: [최근 추가 ▼] |
+--------------------------------------------------------------+
| [장소명] [주소] [삭제] [지도보기] |
| -------------------------------------------------------------|
| 임시 카페 수원시 ... [X] [보기] |
| ... |
+--------------------------------------------------------------+
| 안내: 즐겨찾기는 로그인한 사용자 기준으로 저장됩니다. |
+--------------------------------------------------------------+
⑥ U-05. 최근 검색 기록
+--------------------------------------------------------------+
| (U-05) 최근 검색 기록 |
+--------------------------------------------------------------+
| 기간: [최근 7일 ▼] 사용자: [내 기록 ▼] |
+--------------------------------------------------------------+
| 시간 | 키워드 | 다시검색 |
| -------------------------------------------------------------|
| 2026-02-02 10:12 | 광교 카페 | [검색] |
| 2026-02-01 09:40 | 광교 맛집 | [검색] |
+--------------------------------------------------------------+
| [기록 삭제] (선택) |
+--------------------------------------------------------------+
⑦ U-06. 로그인
+--------------------------------------------------------------+
| (U-06) 로그인 |
+--------------------------------------------------------------+
| 이메일: [_________________________] |
| 비밀번호: [_________________________] |
| [로그인] |
+--------------------------------------------------------------+
| 메시지: "이메일/비밀번호를 확인해 주세요." |
+--------------------------------------------------------------+
7-2. 관리자
① 대시보드
+--------------------------------------------------------------+
| (A-01) 관리자 대시보드 |
+--------------------------------------------------------------+
| [키워드 관리] [지역 프리셋] [자동화 실행 로그] |
+--------------------------------------------------------------+
| 오늘 자동화 실행 요약 |
| - 마지막 run_key: [20260202_090000] |
| - 처리 키워드 수: [3] / 저장 결과 수: [15] |
| - 오류 건수: [0] |
+--------------------------------------------------------------+
② 추천 검색 키워드 관리
+--------------------------------------------------------------+
| (A-02) 추천 키워드 관리 |
+--------------------------------------------------------------+
| 새 키워드: [_________________________] [추가] |
| 상태 필터: [전체 ▼] |
+--------------------------------------------------------------+
| ID | 키워드 | 활성 | 수정 | 비활성/활성전환 |
|--------------------------------------------------------------|
| 3 | 수원 공부하기 좋은 카페 | 1 |[수정]| [OFF] |
| 2 | 광교 맛집 | 1 |[수정]| [OFF] |
| 1 | 광교 카페 | 0 |[수정]| [ON] |
+--------------------------------------------------------------+
| 안내: 자동화는 활성(1) 키워드만 수집합니다. |
+--------------------------------------------------------------+
③ 추천 지역(검색 프리셋) 관리
+--------------------------------------------------------------+
| (A-03) 추천 지역(프리셋) 관리 |
+--------------------------------------------------------------+
| 프리셋명: [광교 ▼] 기본키워드: [카페 ▼] [저장] |
+--------------------------------------------------------------+
| ID | 프리셋명 | 기본키워드 | 설명 | 활성 | 삭제 |
|-------------------------------------------------------------- |
| 1 | 광교 | 카페 | 광교 중심 검색 | 1 | [삭제] |
| 2 | 수원역 | 맛집 | 수원역 주변 | 1 | [삭제] |
+--------------------------------------------------------------+
④ 자동화 실행 로그 목록
+---------------------------------------------------------------------+
| (A-04) 자동화 실행 로그 |
+---------------------------------------------------------------------+
| 기간: [최근 7일 ▼] 검색: [run_key/키워드 __________] [검색] |
+---------------------------------------------------------------------+
| run_key | 실행시간 | 키워드수 | 결과수 | 상세 |
|---------------------------------------------------------------------|
| 20260202_090000 | 2026-02-02 09:00 | 3 | 15 |[보기] |
| 20260201_090000 | 2026-02-01 09:00 | 3 | 15 |[보기] |
+---------------------------------------------------------------------+
⑤ 자동화 실행 상세(run_key 상세)
+------------------------------------------------------------------+
| (A-05) 실행 상세 |
+------------------------------------------------------------------+
| run_key: [20260202_090000] 실행시간: [2026-02-02 09:00] |
| 메모: [morning batch] |
+------------------------------------------------------------------+
| 키워드별 Top3 |
| 키워드 | 1위 | 2위 | 3위 |
|------------------------------------------------------------------|
| 광교 카페 | 장소A | 장소B | 장소C |
| 광교 맛집 | 장소D | 장소E | 장소F |
+------------------------------------------------------------------+
| [장소 캐시 보기] (place_cache 테이블 조회 링크/버튼) |
+------------------------------------------------------------------+
8. ERD 설계와 검증
8-1. ERD
① 핵심 테이블 6개
- users
- admin_keyword
- search_log
- favorite_place
- batch_run
- batch_result
② 스키마 생성
CREATE DATABASE neighborhood_place
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
생성된 스키마 사용
USE neighborhood_place;
③ 테이블 생성
users
CREATE TABLE users (
id BIGING AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
role ENUM('user', 'admin') DEFAULT 'user',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
admin_keyword
CREATE TABLE admin_keyword (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
keyword VARCHAR(100) NOT NULL,
is_active TINYINT(1) DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB;
region_preset
CREATE TABLE region_preset (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
preset_name VARCHAR(50) NOT NULL,
base_keyword VARCHAR(50) NOT NULL,
description VARCHAR(255),
is_active TINYINT(1) DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
place_cache
CREATE TABLE place_cache (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
place_id VARCHAR(50) NOT NULL UNIQUE,
title VARCHAR(255) NOT NULL,
address VARCHAR(255),
road_address VARCHAR(255),
telephone VARCHAR(50),
link VARCHAR(255),
mapx VARCHAR(30),
mapy VARCHAR(30),
keyword VARCHAR(100),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB;
search_log
CREATE TABLE search_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
keyword VARCHAR(100) NOT NULL,
display_count INT DEFAULT 5,
searched_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
) ENGINE=InnoDB;
favorite_place
CREATE TABLE favorite_place (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
place_id VARCHAR(50) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_place (user_id, place_id),
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE,
FOREIGN KEY (place_id) REFERENCES place_cache(place_id)
ON DELETE CASCADE
) ENGINE=InnoDB;
batch_run
CREATE TABLE batch_run (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
run_key VARCHAR(30) NOT NULL UNIQUE,
executed_at DATETIME NOT NULL,
memo VARCHAR(255)
) ENGINE=InnoDB;
batch_result
CREATE TABLE (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
run_id BIGINT NOT NULL,
keyword VARCHAR(100) NOT NULL,
place_id VARCHAR(50) NOT NULL,
rank_no INT NOT NULL,
FOREIGN KEY (run_id) REFERENCES batch_run(id)
ON DELETE CASCADE,
FOREIGN KEY (place_id) REFERENCES place_cache(place_id)
ON DELETE CASCADE
) ENGINE=InnoDB;
SHOW TABLES;
8-2. 샘플 데이터
① 샘플 데이터 추가
users 테이블
INSERT INTO users (email, password, role) VALUES
('admin@test.com', 'admin1234', 'admin'),
('user1@test.com', 'user1234', 'user'),
('user2@test.com', 'user1234', 'user');
admin_keyword 테이블
INSERT INTO admin_keyword (keyword, is_active) VALUES
('광교 카페', 1),
('광교 맛집', 1),
('수원 공부하기 좋은 카페', 1),
('수원 브런치', 0);
region_preset
INSERT INTO region_preset (preset_name, base_keyword, description, is_active)
VALUES
('광교', '카페', '광교 중심 검색', 1),
('광교', '맛집', '광교 맛집 검색', 1),
('수원역', '카페', '수원역 주변 카페', 1),
('수원역', '맛집', '수원역 주변 맛집', 1);
place_cache
INSERT INTO place_cache
(place_id, title, address, road_address, telephone, link, mapx, mapy, keyword)
VALUES
('p1001', '광교카페A', '수원시 영통구 광교동 123',
'수원시 영통구 광교로 12', '031-111-1111',
'https://place.naver.com/1001', '127.0451', '37.2901', '광교 카페'),
('p1002', '광교카페B', '수원시 영통구 광교동 124',
'수원시 영통구 광교로 15', '031-222-2222',
'https://place.naver.com/1002', '127.0462', '37.2912', '광교 카페'),
('p1003', '광교카페C', '수원시 영통구 광교동 125',
'수원시 영통구 광교로 18', '031-333-3333',
'https://place.naver.com/1003', '127.0473', '37.2923', '광교 카페'),
('p2001', '광교맛집A', '수원시 영통구 광교동 201',
'수원시 영통구 광교중앙로 22', '031-444-4444',
'https://place.naver.com/2001', '127.0484', '37.2934', '광교 맛집'),
('p2002', '광교맛집B', '수원시 영통구 광교동 202',
'수원시 영통구 광교중앙로 25', '031-555-5555',
'https://place.naver.com/2002', '127.0495', '37.2945', '광교 맛집'),
('p3001', '수원공부카페A', '수원시 장안구 101',
'수원시 장안구 정조로 10', '031-666-6666',
'https://place.naver.com/3001', '127.0101', '37.3001', '수원 공부하기 좋은 카페');
search_log
INSERT INTO search_log (user_id, keyword, display_count) VALUES
(2, '광교 카페', 5),
(2, '광교 맛집', 5),
(3, '수원 공부하기 좋은 카페', 10),
(2, '광교 카페', 10);
favorite_place
INSERT INTO favorite_place (user_id, place_id) VALUES
(2, 'P1001'),
(2, 'P2001'),
(3, 'P3001');
batch_run
INSERT INTO batch_run (run_key, executed_at, memo) VALUES
('20260201_090000', '2026-02-01 09:00:00', 'morning batch'),
('20260202_090000', '2026-02-02 09:00:00', 'morning batch');
batch_result
runid=1인 경우
INSERT INTO batch_result (run_id, keyword, place_id, rank_no) VALUES
(1, '광교 카페', 'p1001', 1),
(1, '광교 카페', 'p1002', 2),
(1, '광교 카페', 'p1003', 3),
(1, '광교 맛집', 'p2001', 1),
(1, '광교 맛집', 'P2002', 2);
runid=2인 경우
INSERT INTO batch_result (run_id, keyword, place_id, rank_no) VALUES
(2, '광교 카페', 'P1002', 1),
(2, '광교 카페', 'P1001', 2),
(2, '광교 카페', 'P1003', 3),
(2, '광교 맛집', 'P2002', 1),
(2, '광교 맛집', 'P2001', 2);
② 검증 SELECT 쿼리
SELECT * FROM search_log ORDER BY searched_at DESC;
테이블만 바꿔서 검증해주면 됩니다
※ 주의사항
네이버 지역 검색 API의 mapx/mapy는
바로 위도/경도가 아닐 수 있습니다.
☞ 즉, 응답값을 그대로 지도에 찍으면 위치가 이상하게 표시될 수 있습니다!
지도에 마커를 표시하기 전, 좌표 변환 또는 지오 코딩(주소→위경도변환)이 필요할 수 있으니
반드시 좌표 체계를 확인해야 합니다.
→ place_cache 테이블의 좌표 체계 확인

mapx, mapy 좌표 컬럼이 VARCHAR로 되어있으면 지도 마커가 제대로 안 찍히는 이슈가 발생한다...!
그래서 Safe Mode를 해지하고 decimal로 변경해주었습니다
① 먼저 기존 DB에 좌표를 숫자 변환
SET SQL_SAFE_UPDATES = 0;
UPDATE place_cache
SET mapx = mapx / 10000000,
mapy = mapy / 10000000
WHERE CAST(mapx AS DECIMAL(15, 7)) > 1000;
SET SQL_SAFE_UPDATES = 1;
② 컬럼 타입 DECIMAL로 변경
ALTER TABLE place_cache
MODIFY mapx DECIMAL(10, 7);
ALTER TABLE place_cache
MODIFY mapy DECIMAL(10, 7);
좌표 정밀도를 위해 DECIMAL(10, 7)로 소수점 7자리까지 표시해줍니다
③ PHP 코드 수정 (search.php)
기존 코드 속 mapx, mapy를
$item['mapx']
$item['mapy']
아래와 같이 변환하여 저장해야 함
$mapx = $item['mapx'] / 1000000;
$mapy = $item['mapy'] / 1000000;
☞ API 저장 부분 완성
$title = strip_tags($item['title']);
$address = $item['address'];
$roadAddress = $item['roadAddress'];
$telephone = $item['telephone'];
$link = $item['link'];
$mapx = $item['mapx'] / 10000000;
$mapy = $item['mapy'] / 10000000;
④ 최종 확인 쿼리
SELECT title, mapx, mapy
FROM place_cache
ORDER BY created_at DESC
LIMIT 10;
☞ 정상 기준 : 소수점 있는 숫자 형태

'개발 기록 > backend' 카테고리의 다른 글
| 네이버 검색 api를 활용한 뉴스 수집방법 - 키워드 뉴스 요약 구현하기(예제) (0) | 2026.02.25 |
|---|---|
| Mac에서 MySQL 설치방법 및 접속 오류 해결(커넥션 테스트까지) (1) | 2025.03.19 |
| Mac OS에 python3 설치, 가상환경에 Django 설치, 동일한 가상환경 복사해 만들기 (0) | 2025.03.12 |
| 에러 해결) ER_NOT_SUPPORTED_AUTH_MODE : Node.js에서 MySQL을 접근할 때 발생하는 에러 (0) | 2024.10.10 |
| [백엔드] 프리온보딩 - AWS, docker 사전미션 (3) | 2024.02.21 |