{"id":25097189,"url":"https://github.com/y-solb/foliohub-backend","last_synced_at":"2026-04-10T14:41:41.767Z","repository":{"id":226519071,"uuid":"757307120","full_name":"y-solb/foliohub-backend","owner":"y-solb","description":"나만의 포트폴리오 사이트를 만들어 볼까요? (backend)","archived":false,"fork":false,"pushed_at":"2024-07-19T15:37:43.000Z","size":285,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T02:46:29.246Z","etag":null,"topics":["cloudinary","docker","express","jwt","nodejs","postgres","typeorm","typescript"],"latest_commit_sha":null,"homepage":"https://www.foliohub.me","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/y-solb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-14T08:19:53.000Z","updated_at":"2024-07-19T15:37:47.000Z","dependencies_parsed_at":"2024-03-29T10:24:05.625Z","dependency_job_id":"3c019005-19ff-4cd5-8e28-2aef15c7dcf1","html_url":"https://github.com/y-solb/foliohub-backend","commit_stats":null,"previous_names":["y-solb/foliohub-backend"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/y-solb/foliohub-backend","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-solb%2Ffoliohub-backend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-solb%2Ffoliohub-backend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-solb%2Ffoliohub-backend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-solb%2Ffoliohub-backend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/y-solb","download_url":"https://codeload.github.com/y-solb/foliohub-backend/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-solb%2Ffoliohub-backend/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264250642,"owners_count":23579557,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudinary","docker","express","jwt","nodejs","postgres","typeorm","typescript"],"created_at":"2025-02-07T17:30:24.909Z","updated_at":"2025-12-30T20:06:42.274Z","avatar_url":"https://github.com/y-solb.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003ca href=\"https://www.foliohub.me\"\u003e\u003cimg src=\"https://github.com/y-solb/foliohub-backend/assets/59462108/8f74737b-07b0-468e-aea3-acf56d8fb233\" align=\"left\" width=\"40\" height=\"40\"\u003e\u003c/a\u003e Foliohub - Backend\n\n[![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fy-solb%2Ffoliohub-backend\u0026count_bg=%23607AE9\u0026title_bg=%236A6A6A\u0026icon=\u0026icon_color=%23FF0202\u0026title=hits\u0026edge_flat=false)](https://hits.seeyoufarm.com)\n\n나만의 포트폴리오를 만들고 다른 사람들과 공유할 수 있는 서비스\n\n## 💁🏻‍♀️ 소개\n\n![main](https://github.com/y-solb/foliohub-backend/assets/59462108/1c17f2c2-da2a-477d-b9a6-65960393bb04)\n![list](https://github.com/y-solb/foliohub-backend/assets/59462108/99ca6a66-1e10-436f-87db-721045bbb44d)\n![mypage](https://github.com/y-solb/foliohub-backend/assets/59462108/c5d767d8-8242-4c56-b9fb-57ab19bc45e2)\n![asset1](https://github.com/y-solb/foliohub-backend/assets/59462108/d06a3055-d4b5-41b5-ba12-4afaf1ec6ac9)\n![experience](https://github.com/y-solb/foliohub-backend/assets/59462108/1f2ff8a8-b5c8-4bb1-adce-7e4637ca072d)\n\n### [🚀 서비스 보러가기](https://www.foliohub.me)\n\n로그인 없이 체험 코드를 입력하고 체험해 볼 수 있어요\n\n```\n체험코드 : HelloWorld\n```\n\n## ⚒️ 기술 스택\n\n- Language: Typescript\n- Framework : Express\n- Database : PostgreSQL\n- ORM : TypeORM\n- Deploy: AWS EC2, Docker\n\n## 💿 ERD\n\n![ERD](https://github.com/y-solb/foliohub-backend/assets/59462108/b14286e0-8239-4359-8bca-44575c81a6de)\n\n## ✏️ 구현 사항\n\n**로그인/인증**\n\n![인증로직](https://github.com/y-solb/foliohub-backend/assets/59462108/832e2529-25ef-4ba7-afc2-1a10fcea0bad)\n\n- Google 소셜 로그인을 구현하였습니다.\n- 사용자가 로그인하면 Google로부터 받은 정보를 확인하고, 기존 회원인 경우 accessToken과 refreshToken을 쿠키를 통해 발급합니다. 또한, AuthToken 테이블에 userId, refreshToken, 만료일을 저장하여 accessToken 재발급 시 이를 활용합니다.\n- 보안을 강화하기 위해 쿠키는 `httpOnly`, `secure`, `sameSite` 옵션을 설정했습니다.\n- accessToken은 1시간, refreshToken은 14일 뒤에 만료되며, accessToken 만료 시 refreshToken으로 재발급 받을 수 있도록 구현되었습니다. 또한, accessToken에는 userId가 포함되어 있어서 인증 middleware에서 accessToken을 decode하여 userId를 확인할 수 있습니다.\n- 로그인 후 회원가입이 필요한 경우, 회원가입 페이지로 리다이렉트합니다. 이때 사용자 정보(email, provider, providerId)를 토큰으로 생성하고 쿠키에 저장하여 회원가입 시 사용할 수 있도록 구현하였습니다.\n- 인증이 필요한 API의 경우 middleware를 거칩니다. 여기서 accessToken과 refreshToken을 확인하여, 모두 있는 경우에만 accessToken을 decode하여 사용자의 정보를 가져옵니다. 이 정보를 요청(req) 객체에 추가하여 이후의 작업에서도 사용할 수 있도록 했습니다.\n- 로그아웃 시 accessToken과 refreshToken을 쿠키에서 삭제했습니다.\n\n**이미지 업로드**\n\n- 이미지를 사용 목적에 따라 폴더를 나누어 이미지 업로드 시 이미지의 타입(thumbnail, asset)을 받아와 해당 타입의 폴더 안에 저장했습니다.\n- 이미지는 multer를 사용하여 서버의 uploads 폴더에 저장되며 이후에는 Cloudinary를 통해 업로드됩니다. 이미지 최적화를 위해 넓이 1000px로 크롭하여 저장했습니다. 업로드 후에는 서버에서 업로드 된 이미지 파일을 삭제했습니다.\n- 프론트엔드 개발 환경에서는 HTTP를 사용하고 배포된 환경에서 HTTPS를 사용하기 때문에 이미지를 상대 경로로 저장했습니다.\n\n**직업 카테고리 테이블**\n\n\u003cimg src=\"https://github.com/y-solb/foliohub-backend/assets/59462108/2a1fd219-d9ab-45ef-9ed4-d4612dba7be0\" alt=\"직업 카테고리 테이블\" width=\"280\"\u003e\n\n- 데이터를 구조화하여 관리하고 검색 및 필터링을 용이하게 하고자 직업을 카테고리별로 상위 카테고리(01-개발)와 하위 카테고리(01001-웹개발자)로 분류하여 테이블에 저장했습니다.\n\n**좋아요**\n\n- 좋아요를 누른 경우 LikePortfolio 테이블에 좋아요를 누른 사용자 id, 좋아요를 받은 포트폴리오의 id와 상태 값을 저장하고 Portfolio 테이블에서 해당 포트폴리오의 좋아요 수를 업데이트했습니다.\n- 좋아요를 취소하는 경우 좋아요 여부를 false로 변경하고 Portfolio 테이블에서 해당 포트폴리오의 좋아요 수를 업데이트했습니다.\n\n**CORS**\n\n- cors 미들웨어로 출처가 다른 요청을 허용하고 허용된 메서드 및 헤더를 설정했습니다. 쿠키 값을 가져오기 위해 `credentials: true` 옵션을 설정했습니다.\n\n**배포**\n\n- Docker를 활용하여 AWS EC2에 PM2로 무중단 배포를 구현했습니다.\n- AWS Certificate Manager를 사용하여 SSL 인증서를 발급받고, 이를 Route 53을 통해 설정한 Load Balancer에 연결하여 HTTPS를 구현했습니다.\n\n## ⛳️ 실행\n\n### 환경변수 설정\n\n```\n# .env.development\nPORT=3001\nNODE_ENV=development\nORIGIN=http://localhost:3000\nAPP_URL=http://localhost:3001\n\n# JWT\nJWT_SECRET_KEY=\n\n# COOKIE DOMAIN\nCOOKIE_DOMAIN=\n\n# GOOGLE\nGOOGLE_CLIENT_ID=\nGOOGLE_CLIENT_SECRET=\n\n# CLOUDINARY\nCLOUDINARY_UPLOAD_PRESET=\nCLOUDINARY_CLOUD_NAME=\nCLOUDINARY_API_KEY=\nCLOUDINARY_API_SECRET=\nCLOUDINARY_BASE_URL=http://res.cloudinary.com/...../image/upload\n```\n\n```\n# .env\n# DB\nPOSTGRES_USER=\nPOSTGRES_PASSWORD=\nPOSTGRES_DB=\n```\n\n### 설치 및 시작\n\n```\ndocker compose up\n```\n\n## 🗂️ 관련 링크\n\n- [Foliohub Front Repository](https://github.com/y-solb/foliohub-frontend)\n- [1차 완성 회고](https://sollogging.tistory.com/83)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy-solb%2Ffoliohub-backend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fy-solb%2Ffoliohub-backend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy-solb%2Ffoliohub-backend/lists"}