Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 0 additions & 33 deletions .github/workflows/deploy.yml

This file was deleted.

6 changes: 3 additions & 3 deletions .github/workflows/docker-img.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
file: ./apps/express-server/Dockerfile
file: ./Dockerfile
push: true
tags: |
asia-northeast3-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/project/express-server:${{ github.sha }}
asia-northeast3-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/project/express-server:latest
asia-northeast3-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/medi-map/express-server:${{ github.sha }}
asia-northeast3-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/medi-map/express-server:latest
cache-from: type=gha,scope=express-server
cache-to: type=gha,mode=max,scope=express-server
build-args: |
Expand Down
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM node:20-alpine3.18

WORKDIR /app

# 1) 루트에 있는 주요 설정/패키지 파일들 복사
COPY turbo.json .
COPY tsconfig.json .
COPY package.json .

# 2) express-server 폴더 통째로 복사
COPY apps/express-server ./apps/express-server

# 3) 의존성 설치 & 빌드
RUN yarn
RUN yarn build

# 4) 실행
CMD ["yarn", "workspace", "express-server", "start"]
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<img src="https://img.shields.io/badge/Express.js-000000?style=for-the-badge&logo=express&logoColor=white" alt="Express.js"/> <img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript"/> <img src="https://img.shields.io/badge/PostgreSQL-4169E1?style=for-the-badge&logo=postgresql&logoColor=white" alt="PostgreSQL"/> <img src="https://img.shields.io/badge/Sequelize-52B0E7?style=for-the-badge&logo=sequelize&logoColor=white" alt="Sequelize"/> <img src="https://img.shields.io/badge/JWT-000000?style=for-the-badge&logo=jsonwebtokens&logoColor=white" alt="JWT"/> <img src="https://img.shields.io/badge/Google_Cloud_Platform-4285F4?style=for-the-badge&logo=google-cloud&logoColor=white" alt="Google Cloud Platform"/>

### 프론트엔드
<img src="https://img.shields.io/badge/Next.js-000000?style=for-the-badge&logo=next.js&logoColor=white" alt="Next.js"/> <img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript"/> <img src="https://img.shields.io/badge/Zustand-000000?style=for-the-badge&logo=zustand&logoColor=white" alt="Zustand"/> <img src="https://img.shields.io/badge/SCSS-CC6699?style=for-the-badge&logo=sass&logoColor=white" alt="SCSS"/> <img src="https://img.shields.io/badge/Tailwind_CSS-06B6D4?style=for-the-badge&logo=tailwindcss&logoColor=white" alt="Tailwind CSS"/> <img src="https://img.shields.io/badge/Axios-5A29E4?style=for-the-badge&logo=axios&logoColor=white" alt="Axios"/>
<img src="https://img.shields.io/badge/Next.js-000000?style=for-the-badge&logo=next.js&logoColor=white" alt="Next.js"/> <img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript"/> <img src="https://img.shields.io/badge/Zustand-000000?style=for-the-badge&logo=zustand&logoColor=white" alt="Zustand"/> <img src="https://img.shields.io/badge/SCSS-CC6699?style=for-the-badge&logo=sass&logoColor=white" alt="SCSS"/> <img src="https://img.shields.io/badge/NextAuth.js-000000?style=for-the-badge&logo=next.js&logoColor=white" alt="NextAuth.js"/> <img src="https://img.shields.io/badge/TanStack_Query-FF4154?style=for-the-badge&logo=react-query&logoColor=white" alt="TanStack Query"/> <img src="https://img.shields.io/badge/Axios-5A29E4?style=for-the-badge&logo=axios&logoColor=white" alt="Axios"/>

### 배포 및 인프라
<img src="https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white" alt="Docker"/> <img src="https://img.shields.io/badge/GCP_App_Engine-4285F4?style=for-the-badge&logo=google-cloud&logoColor=white" alt="Google Cloud Platform"/> <img src="https://img.shields.io/badge/GCP_Cloud_SQL-4285F4?style=for-the-badge&logo=google-cloud&logoColor=white" alt="Google Cloud Platform"/> <img src="https://img.shields.io/badge/GitHub_Actions-2088FF?style=for-the-badge&logo=github-actions&logoColor=white" alt="GitHub Actions"/>
Expand All @@ -19,20 +19,26 @@
## 주요 기능🚀

### 1. 회원가입 및 로그인
- JWT 인증 기반 회원가입로그인 기능 구현
- NextAuth와의 통합
- NextAuth.js를 활용한 Google OAuth자체 로그인 구현
- JWT 기반 인증 시스템

### 2. 의약품 검색
### 2. 의약품 검색
- 공공 API 연동을 통한 의약품 정보 제공
- 캐싱을 통한 응답 속도 개선

### 3. 약국 정보 제공
### 3. 약국 정보 제공
- 사용자의 현재 위치 기반 가까운 약국 검색
- 카카오맵 연동
- 반경 조정 및 지도 내 검색 기능

### 4. 마이페이지
### 4. 마이페이지
- 사용자 정보 수정(닉네임, 비밀번호)
- 인증 토큰을 활용한 보안 강화
- 즐겨찾기 약물 리스트 확인 및 삭제제

### 5. CI/CD 및 배포
- GitHub Actions를 통한 자동화된 빌드 및 배포
- Docker 컨테이너화를 통한 개발/운영 환경 일관성 유지
- GCP App Engine 및 Cloud SQL을 활용한 클라우드 인프라 구축

## 로컬 개발 실행 방법⚙️
### 백엔드
Expand Down
24 changes: 24 additions & 0 deletions app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
runtime: custom
env: flex
service: backend

manual_scaling:
instances: 1

resources:
cpu: 1
memory_gb: 0.5
disk_size_gb: 10

env_variables:
JWT_SECRET: 'b1fbf862fe5538e821c12ac9c45c9809f87e685e04'
DB_USER: 'postgres'
DB_HOST: '34.132.121.229'
DB_NAME: 'postgres'
DB_PASSWORD: 'postgres'
DB_PORT: 5432
FRONTEND_URL: 'https://medi-map-next-client.vercel.app'
DATA_API_KEY: 'YJ8so8uNR%2FiHv%2FB3FhjsGfy%2BxFhgfB9tv9grEqDfzPkkoWEx8tIiME6UarZefKqWPeHo%2BE3XxrtqA8BH0WOlnQ%3D%3D'

readiness_check:
app_start_timeout_sec: 600
27 changes: 0 additions & 27 deletions apps/express-server/Dockerfile

This file was deleted.

28 changes: 0 additions & 28 deletions apps/express-server/app.yaml

This file was deleted.

11 changes: 4 additions & 7 deletions apps/express-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express": "^4.21.2",
"fast-xml-parser": "^4.5.0",
"googleapis": "^144.0.0",
"jsdom": "^26.0.0",
"jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"multer": "^1.4.5-lts.1",
Expand All @@ -33,18 +34,14 @@
"uuid": "^11.0.3",
"winston": "3.13.0"
},
"resolutions": {
"@types/express": "4.17.21",
"@types/express-serve-static-core": "4.19.6",
"@types/multer": "1.4.1"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/cors": "^2.8.17",
"@types/dompurify": "^3.2.0",
"@types/dotenv": "^8.2.0",
"@types/express": "4.17.21",
"@types/jsdom": "^21.1.7",
"@types/jsonwebtoken": "^9.0.7",
"@types/multer": "1.4.1",
"@types/node": "^20.14.9",
"@types/pg": "^8.11.10",
"@types/redis": "^4.0.11",
Expand Down
36 changes: 22 additions & 14 deletions apps/express-server/src/controllers/signupController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,35 @@ import { createUser, findUserByEmail } from '@/services/authService';
import { generateAccessToken, generateRefreshToken } from '@/utils/generateToken';
import { AUTH_MESSAGES } from '@/constants/auth_message';
import { storeRefreshToken } from '@/services/refreshTokenService';
import { validateUser } from '@medi-map/validation';

// 유효성 검사 함수
const validateSignupInput = (username: string, email: string, password: string): string | null => {
if (!username || username.length < 3 || username.length > 30) {
return AUTH_MESSAGES.USERNAME_INVALID;
}

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !emailRegex.test(email)) {
return AUTH_MESSAGES.EMAIL_INVALID;
}

if (!password || password.length < 8) {
return AUTH_MESSAGES.PASSWORD_INVALID;
}

return null;
};

// 회원가입 처리
export const signup = async (req: Request, res: Response): Promise<Response> => {
const { username, email, password } = req.body;

try {
// 유효성 검사
const validationResult = validateUser(req.body);

if (!validationResult.success) {
const flattenedErrors = validationResult.error.flatten().fieldErrors;

console.error('Validation error:', flattenedErrors);

const [firstField, firstMessages] = Object.entries(flattenedErrors)[0];

return res.status(400).json({
message: firstMessages[0],
field: firstField
});
const validationError = validateSignupInput(username, email, password);
if (validationError) {
console.error('Validation error:', validationError);
return res.status(400).json({ message: validationError });
}

// 이메일 중복 확인
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,9 @@ module.exports = {
defaultValue: Sequelize.fn('NOW'),
},
});

await queryInterface.addIndex('Medicine', ['itemName'], {
name: 'Medicine_itemName_index',
});

await queryInterface.addIndex('Medicine', ['entpName'], {
name: 'Medicine_entpName_index',
});

await queryInterface.addIndex('Medicine', ['colorClass1', 'drugShape'], {
name: 'Medicine_colorShape_index',
});
},

async down(queryInterface, Sequelize) {
await queryInterface.removeIndex('Medicine', 'Medicine_itemName_index');
await queryInterface.removeIndex('Medicine', 'Medicine_entpName_index');
await queryInterface.removeIndex('Medicine', 'Medicine_colorShape_index');

await queryInterface.dropTable('Medicine');
},
};

This file was deleted.

Loading
Loading