{"id":25019742,"url":"https://github.com/donghl-dev/reactive-blog","last_synced_at":"2026-05-01T06:32:53.123Z","repository":{"id":126966256,"uuid":"224583413","full_name":"donghL-dev/Reactive-Blog","owner":"donghL-dev","description":"Reactive-Programming을 이용한 비동기 Blog API 프로젝트","archived":false,"fork":false,"pushed_at":"2023-03-15T11:33:34.000Z","size":174,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-30T06:05:06.214Z","etag":null,"topics":["junit5","reactive-programming","reactivemongo","spring-boot","spring-security","spring-webflux"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/donghL-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2019-11-28T06:17:46.000Z","updated_at":"2023-03-15T11:33:39.000Z","dependencies_parsed_at":"2023-06-19T09:37:44.331Z","dependency_job_id":null,"html_url":"https://github.com/donghL-dev/Reactive-Blog","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/donghL-dev/Reactive-Blog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/donghL-dev%2FReactive-Blog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/donghL-dev%2FReactive-Blog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/donghL-dev%2FReactive-Blog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/donghL-dev%2FReactive-Blog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/donghL-dev","download_url":"https://codeload.github.com/donghL-dev/Reactive-Blog/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/donghL-dev%2FReactive-Blog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32487408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["junit5","reactive-programming","reactivemongo","spring-boot","spring-security","spring-webflux"],"created_at":"2025-02-05T11:51:30.464Z","updated_at":"2026-05-01T06:32:53.108Z","avatar_url":"https://github.com/donghL-dev.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"Project - Reactive Blog\n===\n\n\u003e 리액티브 프로그래밍의 학습을 위해서 `Spring-WebFlux`, `Mono/Flux`, `Reactive-Monogo`를 이용하여 진행하는 비동기 Blog API 프로젝트. \n\n## 개발환경\n\n|도구|버전|\n|:---:|:---:|\n| Framework |Spring Boot 2.2.1 |\n| OS |Windows 10, Ubuntu 18.04|\n| IDE |IntelliJ IDEA Ultimate |\n| JDK |JDK 1.8+|\n| DataBase |MongoDB Server 4.2.1|\n| Build Tool | Gradle 4.8.1 |\n\n## 개발 방법\n\u003cdetails\u003e\u003csummary\u003e세부정보\u003c/summary\u003e\n\n* 개발과 관련된 모든 이야기는 [Issues](https://github.com/donghL-dev/Reactive-Blog/issues)에서 진행합니다.\n\n* `API` 및 도메인 명세를 기반으로 개발을 진행하며, 명세에 변경사항이 생길 경우 빠른 시일내에 최신화 합니다.\n\n* **Fork**를 통한 `PR`을 지향합니다.\n\n* 아래와 같은 `Git Workflow`를 지향하며 지키려고 노력합니다. ([참고](https://nvie.com/posts/a-successful-git-branching-model/?))\n\n    \u003cimg width=750, height=850, src=\"https://camo.githubusercontent.com/7f2539ff6001fe7700853313e7cdb7fd4602e16a/68747470733a2f2f6e7669652e636f6d2f696d672f6769742d6d6f64656c4032782e706e67\"\u003e\n\n\u003c/details\u003e\n\n## 실행 방법\n\u003cdetails\u003e\u003csummary\u003e세부정보\u003c/summary\u003e\n\n* 준비사항.\n\n    * `Gradle` or `IntelliJ IDEA`\n\n    * `JDK` (\u003e= 1.8)\n\n    * `Spring Boot` (\u003e= 2.x)\n\n* 저장소를 `clone`\n\n    ```bash\n    $ git clone https://github.com/donghL-dev/Reactive-Blog.git\n    ```\n\n* 데이터 베이스는 `MongoDB`를 사용해야 합니다.\n\n* 프로젝트 내 `Reactive-Blog\\src\\main\\resources` 경로에 `application.yml` 생성.\n\n    * 밑의 양식대로 내용을 채운 뒤, `application.yml`에 삽입.\n    \u003cbr\u003e\n\n    ```yml\n    spring:\n        data:\n            mongodb:\n                host: # 본인의 DB 서버 주소를 넣으면 되는데, 왠만하면 localhost입니다.\n                port: # 본인의 DB 서버 PORT 왠만하면 27017입니다.\n                database: # 본인의 데이터베이스 이름을 기재하시면 됩니다.\n    ```\n\n* `IntelliJ IDEA`(\u003e= 2018.3)에서 해당 프로젝트를 `Open`\n\n    * 또는 터미널을 열어서 프로젝트 경로에 진입해서 다음 명령어를 실행.\n\n    * `Windows 10`\n\n        ```bash\n        $ gradlew bootRun\n        ```\n\n    * `Ubuntu 18.04`\n\n        ```\n        $ ./gradlew bootRun\n        ```\n\n\u003c/details\u003e\n\n## Dependencies\n\u003cdetails\u003e\u003csummary\u003e세부정보\u003c/summary\u003e\n\n* `Spring Reactive Web`\n\n* `Spring Data Reactive MongoDB`\n\n* `Embedded MongoDB Database`\n\n* `Spring Security`\n\n* `Lombok`\n\n\u003c/details\u003e\n\n## 도메인 명세\n\n* [도메인 명세 문서](https://www.notion.so/dhlab/52ff6bb691934fbabeca5287bc32dffb)\n\n## API 명세 \n\u003cdetails\u003e\u003csummary\u003e세부정보\u003c/summary\u003e\n\n* 모든 `API`에 대한 반환은 `Content-Type: application/json; charset=utf-8`를 기본으로 합니다.\n\n* 인증(`auth`)은 `HTTP` 헤더를 사용해서 진행됩니다.\u003cbr\u003e\n\n    | Key | Value |\n    |:---:|:---:|\n    | Content-Type | `application/json` |\n    | Authorization | `token` |\n\n* `Response`\n\n    * `User`\n\n        ```json\n        {\n            \"user\": {\n                \"email\": \"...\",\n                \"token\": \"...\",\n                \"username\": \"...\",\n                \"bio\": \"...\",\n                \"image\": null\n            }\n        }\n        ```\n    \n    * `Profile`\n\n        ```json\n        {\n            \"profile\": {\n                \"username\": \"...\",\n                \"bio\": \"...\",\n                \"image\": \"...\",\n                \"following\": false\n            }\n        }\n        ```\n\n    * `Single Article`\n\n        ```json\n        {\n            \"article\": {\n                \"slug\": \"...\",\n                \"title\": \"...\",\n                \"description\": \"...?\",\n                \"body\": \"...\",\n                \"tagList\": [\"...\", \"...\"],\n                \"createdAt\": \"9999-99-99T00:00:00.000Z\",\n                \"updatedAt\": \"9999-99-99T00:00:00.000Z\",\n                \"favorited\": false,\n                \"favoritesCount\": 0,\n                \"author\": {\n                    \"username\": \"...\",\n                    \"bio\": \"...\",\n                    \"image\": \"...\",\n                    \"following\": false\n                }\n            }\n        }\n        ```\n    \n    * `Multiple Article`\n\n        ```json\n        {\n            \"articles\":[{\n                \"slug\": \"...\",\n                \"title\": \"...\",\n                \"description\": \"...?\",\n                \"body\": \"...\",\n                \"tagList\": [\"...\", \"...\"],\n                \"createdAt\": \"9999-99-99T00:00:00.000Z\",\n                \"updatedAt\": \"9999-99-99T00:00:00.000Z\",\n                \"favorited\": false,\n                \"favoritesCount\": 0,\n                \"author\": {\n                    \"username\": \"...\",\n                    \"bio\": \"...\",\n                    \"image\": \"...\",\n                    \"following\": false\n                }\n            }, {\n                \"slug\": \"...\",\n                \"title\": \"...\",\n                \"description\": \"...?\",\n                \"body\": \"...\",\n                \"tagList\": [\"...\", \"...\"],\n                \"createdAt\": \"9999-99-99T00:00:00.000Z\",\n                \"updatedAt\": \"9999-99-99T00:00:00.000Z\",\n                \"favorited\": false,\n                \"favoritesCount\": 0,\n                \"author\": {\n                    \"username\": \"...\",\n                    \"bio\": \"...\",\n                    \"image\": \"...\",\n                    \"following\": false\n                }\n            }],\n            \"articlesCount\": 2\n        }\n        ```\n\n    * `Single Comment`\n\n        ```json\n        {\n            \"comment\": {\n                \"id\": 1,\n                \"createdAt\": \"9999-99-99T00:00:00.000Z\",\n                \"updatedAt\": \"9999-99-99T00:00:00.000Z\",\n                \"body\": \"...\",\n                \"author\": {\n                    \"username\": \"...\",\n                    \"bio\": \"...\",\n                    \"image\": \"...\",\n                    \"following\": false\n                }\n            }\n        }\n        ```\n    \n    * `Multiple Comments`\n\n        ```json\n        {\n            \"comments\": [{\n                \"id\": 1,\n                \"createdAt\": \"9999-99-99T00:00:00.000Z\",\n                \"updatedAt\": \"9999-99-99T00:00:00.000Z\",\n                \"body\": \"...\",\n                \"author\": {\n                    \"username\": \"...\",\n                    \"bio\": \"...\",\n                    \"image\": \"...\",\n                    \"following\": false\n                }\n            },{\n                \"id\": 1,\n                \"createdAt\": \"9999-99-99T00:00:00.000Z\",\n                \"updatedAt\": \"9999-99-99T00:00:00.000Z\",\n                \"body\": \"...\",\n                \"author\": {\n                \"username\": \"...\",\n                \"bio\": \"...\",\n                \"image\": \"...\",\n                \"following\": false\n                }\n            }]\n        }\n        ```\n    \n    * `List of Tags`\n\n        ```json\n        {\n            \"tags\": [\n                \"reactjs\",\n                \"angularjs\"\n            ]\n        }\n        ```\n\n    * `Errors and Status Codes`\n\n        ```json\n        {\n            \"errors\":{\n                \"body\": [\n                    \"...\"\n                ]\n            }\n        }\n        ```\n    \n    * `Default Success Code`\n\n        ```json\n        {\n            \"body\": {\n                \"status\": \"200 OK\",\n                \"message\": \"Your request has been successfully processed.\"\n            }\n        }\n        ```\n\n* 대표적인 에러 코드\n\n    * `401 for Unauthorized requests`\n\n    * `400 for Bad requests`\n\n    * `404 for Not found requests`\n\n* End Point\n\n    * 사용자 및 로그인 \u003cbr\u003e\u003cbr\u003e\n\n    | Title | HTTP Method | URL | Request | Response | Auth\n    |:---:|:---:|:---:|:---:|:---:|:---:|\n    | `Registration` | `POST` | `/api/users` | `{ \"user\":{ \"username\": \"sangkon\", \"email\": \"me@sangkon.com\", \"password\": \"qwer1234\" } }` | `User` | `NO`\n    | `Authentication` | `POST` | `/api/users/login` | `{ \"user\":{ \"email\": \"demo@demo.com\", \"password\": \"X12345678\" } }` | `User` | `No`\n    | `Authentication expiration` | `POST` | `/api/users/logout` |  | `Default Success Code` | `YES`\n    | `Current User` | `GET` | `/api/user` |  | `Current User` | `YES`\n    | `Update User` | `PUT` | `/api/user` | `{ \"user\":{ \"email\": \"me@sangkon.com\", \"bio\": \"Java developer\", \"image\": \"image URL\" } }` | `User` | `YES`\n    | `Get Profile` | `GET` | `/api/profiles/:username` |  | `Profile` | `NO`\n    | `Fallow User` | `POST` | `/api/profiles/:username/follow` |  | `Profile` | `YES`\n    | `Unfallow User` | `DELETE` | `/api/profiles/:username/follow` |  | `Profile` | `YES`\n    \n    * 블로그 내용 \u003cbr\u003e\u003cbr\u003e\n\n    | Title | HTTP Method | URL | Request | Response | Auth\n    |:---:|:---:|:---:|:---:|:---:|:---:|\n    | `List Articles` | `GET` | `/api/articles` |  | `Multiple Articles` | `NO`\n    | `Filter by tag` | `GET` | `/api/articles?tag=springboot` |  | `Multiple Articles` | `NO`\n    | `Filter by author` | `GET` | `/api/articles?author=demo` |  | `Multiple Articles` | `NO`\n    | `Favorited by user` | `GET` | `/api/articles?favorited=demo` |  | `Multiple Articles` | `NO`\n    | `Limit number of articles` | `GET` | `/api/articles?limit=20` |  | `Multiple Articles` | `NO`\n    | `Offset/skip number of articles` | `GET` | `/api/articles?offset=0` |  | `Multiple Articles` | `NO`\n    | `Feed Articles` | `GET` | `/api/articles/feed` |  | `Multiple Articles` | `YES`\n    | `Get Articles` | `GET` | `/api/articles/:slug` |  | `Single article` | `YES`\n    | `Create Article` | `POST` | `/api/articles` | `{ \"article\": { \"title\": \"How to train your dragon\", \"description\": \"Ever wonder how?\", \"body\": \"You have to believe\", \"tagList\": [\"reactjs\", \"angularjs\", \"dragons\"] } }` | `Single article` | `YES`\n    | `Update Article` | `PUT` | `/api/articles/:slugs` | `{ \"article\": { \"title\": \"Did you train your dragon?\" } }` | `Single article` | `YES`\n    | `Delete Article` | `DELETE` | `/api/articles/:slug` | | | `YES`\n    | `Add Comments to an Article` | `POST` | `/api/articles/:slug/comments` | `{ \"comment\": { \"body\": \"His name was my name too.\" } }` | `Single Comment` | `YES`\n    | `Get Comments from an Article` | `GET` | `/api/articles/:slug/comments` | | `Multiple comments` | `NO`\n    | `Delete Comment` | `DELETE` | `/api/articles/:slug/comments/:id` | | | `YES`\n    | `Favorite Article` | `POST` | `/api/articles/:slug/favorite` | | `Single article` | `YES`\n    | `Unfavorite Article` | `DELETE` | `/api/articles/:slug/favorite` | | `Single article` | `YES`\n    | `Get Tags` | `GET` | `/api/tags` | | `List of Tags` | `NO`\n\n\u003c/details\u003e\n\n## Reference\n\u003cdetails\u003e\u003csummary\u003e세부정보\u003c/summary\u003e\n\u003cbr\u003e\n\n* [Spring Seucrity 적용 및 JWT 토큰 관련 참고 페이지](https://medium.com/@ard333/authentication-and-authorization-using-jwt-on-spring-webflux-29b81f813e78?)\n\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdonghl-dev%2Freactive-blog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdonghl-dev%2Freactive-blog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdonghl-dev%2Freactive-blog/lists"}