From 1cf5010fdce28bd331f030c53da578913eaa82d4 Mon Sep 17 00:00:00 2001 From: yechop Date: Thu, 26 Sep 2024 14:50:53 +0900 Subject: [PATCH 01/74] =?UTF-8?q?refactor:=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20=EC=95=A1=EC=84=B8=EC=8A=A4?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReferenceLinkController.java | 2 +- .../repository/OpenGraphRepository.java | 2 +- .../repository/ReferenceLinkRepository.java | 6 ++++ .../service/OpenGraphService.java | 4 +-- .../service/ReferenceLinkService.java | 9 ++++-- .../service/OpenGraphServiceTest.java | 4 +-- .../service/ReferenceLinkServiceTest.java | 29 ++++++++++++++----- 7 files changed, 40 insertions(+), 16 deletions(-) diff --git a/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java b/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java index 940ee83b0..5dabd824f 100644 --- a/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java +++ b/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java @@ -63,7 +63,7 @@ public ResponseEntity deleteReferenceLink( @PathVariable("accessCode") final String accessCodeText, @PathVariable("id") final long id ) { - referenceLinkService.deleteReferenceLink(id); + referenceLinkService.deleteReferenceLink(accessCodeText, id); return ResponseEntity.noContent() .build(); diff --git a/backend/src/main/java/site/coduo/referencelink/repository/OpenGraphRepository.java b/backend/src/main/java/site/coduo/referencelink/repository/OpenGraphRepository.java index 883e6b69d..e1df1a33f 100644 --- a/backend/src/main/java/site/coduo/referencelink/repository/OpenGraphRepository.java +++ b/backend/src/main/java/site/coduo/referencelink/repository/OpenGraphRepository.java @@ -6,7 +6,7 @@ public interface OpenGraphRepository extends JpaRepository { - void deleteByReferenceLinkEntityId(Long referenceLinkEntityId); + void deleteByReferenceLinkEntity(ReferenceLinkEntity referenceLinkEntity); Optional findByReferenceLinkEntityId(Long id); } diff --git a/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java b/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java index 666ea5dc3..786902594 100644 --- a/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java +++ b/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java @@ -2,6 +2,12 @@ import org.springframework.data.jpa.repository.JpaRepository; +import site.coduo.referencelink.exception.ReferenceLinkException; + public interface ReferenceLinkRepository extends JpaRepository { + default ReferenceLinkEntity fetchById(long id) { + return findById(id) + .orElseThrow(() -> new ReferenceLinkException("존재하지 않는 링크입니다..")); + } } diff --git a/backend/src/main/java/site/coduo/referencelink/service/OpenGraphService.java b/backend/src/main/java/site/coduo/referencelink/service/OpenGraphService.java index 7a233a405..5f360fc29 100644 --- a/backend/src/main/java/site/coduo/referencelink/service/OpenGraphService.java +++ b/backend/src/main/java/site/coduo/referencelink/service/OpenGraphService.java @@ -69,7 +69,7 @@ public OpenGraph findOpenGraph(final Long id) { return new OpenGraph(); } - public void deleteByReferenceLinkId(final long referenceLinkEntityId) { - openGraphRepository.deleteByReferenceLinkEntityId(referenceLinkEntityId); + public void deleteByReferenceLink(final ReferenceLinkEntity referenceLinkEntity) { + openGraphRepository.deleteByReferenceLinkEntity(referenceLinkEntity); } } diff --git a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java index cdcec8855..fceb57a4f 100644 --- a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java +++ b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java @@ -118,8 +118,11 @@ private ReferenceLinkResponse makeReferenceLinkResponse(final ReferenceLinkEntit return new ReferenceLinkResponse(referenceLinkEntity, openGraph); } - public void deleteReferenceLink(final long id) { - openGraphService.deleteByReferenceLinkId(id); - referenceLinkRepository.deleteById(id); + public void deleteReferenceLink(final String accessCodeText, final long id) { + final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.fetchById(id); + if (referenceLinkEntity.isSameAccessCode(new AccessCode(accessCodeText))) { + openGraphService.deleteByReferenceLink(referenceLinkEntity); + referenceLinkRepository.delete(referenceLinkEntity); + } } } diff --git a/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java b/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java index ed9b513dc..8117275bc 100644 --- a/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java +++ b/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java @@ -91,7 +91,7 @@ void return_null_when_cannot_find_open_graph() { ); } - @DisplayName("레퍼런스링크 id로 오픈그래프를 삭제한다.") + @DisplayName("레퍼런스링크로 오픈그래프를 삭제한다.") @Test void delete_open_graph_by_reference_link_id() throws MalformedURLException { // given @@ -108,7 +108,7 @@ void delete_open_graph_by_reference_link_id() throws MalformedURLException { openGraphService.createOpenGraph(referenceLinkEntity, url); // when - openGraphService.deleteByReferenceLinkId(referenceLinkEntity.getId()); + openGraphService.deleteByReferenceLink(referenceLinkEntity); // then assertThat(openGraphRepository.findAll()).isEmpty(); diff --git a/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java b/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java index e209a7c4a..837af1f31 100644 --- a/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java +++ b/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java @@ -106,15 +106,12 @@ void search_all_reference_link() throws MalformedURLException { void delete_reference_link_and_open_graph() throws MalformedURLException { // given final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final CategoryEntity category = categoryRepository.save( - new CategoryEntity(pairRoomEntity, new Category("리액트"))); - final ReferenceLinkEntity link = referenceLinkRepository.save( - new ReferenceLinkEntity( - new ReferenceLink(new URL("http://url1.com"), new AccessCode(pairRoomEntity.getAccessCode())), - category, pairRoomEntity)); + final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest("https://www.naver.com", null); + final ReferenceLinkResponse referenceLink = referenceLinkService.createReferenceLink( + pairRoomEntity.getAccessCode(), request); // when - referenceLinkService.deleteReferenceLink(link.getId()); + referenceLinkService.deleteReferenceLink(pairRoomEntity.getAccessCode(), referenceLink.id()); // then assertAll( @@ -122,4 +119,22 @@ void delete_reference_link_and_open_graph() throws MalformedURLException { () -> assertThat(openGraphRepository.findAll()).isEmpty() ); } + + @DisplayName("액세스코드가 일치하지 않으면 삭제를 시도해도 삭제되지 않는다.") + @Test + void cannot_delete_reference_link_and_open_graph_when_invalid_access_code() throws MalformedURLException { + // given + final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); + final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest("https://www.naver.com", null); + final ReferenceLinkResponse referenceLink = referenceLinkService.createReferenceLink( + pairRoomEntity.getAccessCode(), request); + + // when + referenceLinkService.deleteReferenceLink("abcdef", referenceLink.id()); + + assertAll( + () -> assertThat(referenceLinkRepository.findAll()).hasSize(1), + () -> assertThat(openGraphRepository.findAll()).hasSize(1) + ); + } } From 8d480a22e643fe3ffeda8c9916c7aa9716caa2b9 Mon Sep 17 00:00:00 2001 From: yechop Date: Thu, 26 Sep 2024 14:55:40 +0900 Subject: [PATCH 02/74] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20fakeServer=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coduo/referencelink/fake/FakeServer.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 backend/src/test/java/site/coduo/referencelink/fake/FakeServer.java diff --git a/backend/src/test/java/site/coduo/referencelink/fake/FakeServer.java b/backend/src/test/java/site/coduo/referencelink/fake/FakeServer.java new file mode 100644 index 000000000..81718b7d3 --- /dev/null +++ b/backend/src/test/java/site/coduo/referencelink/fake/FakeServer.java @@ -0,0 +1,55 @@ +package site.coduo.referencelink.fake; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +import site.coduo.referencelink.exception.ReferenceLinkException; + +public class FakeServer { + + public static final String testUrl; + + static { + final String html = "" + + "헤드 타이틀" + + "" + + "" + + "" + + ""; + testUrl = "http://localhost:" + createAndStartFakeServer(html) + "/test"; + } + + public static int createAndStartFakeServer(final String html) { + HttpServer server; + try { + server = HttpServer.create(new InetSocketAddress(0), 0); + } catch (final IOException e) { + throw new ReferenceLinkException("테스트용 서버 생성에 실패했습니다."); + } + final int assignedPort = server.getAddress().getPort(); + + server.createContext("/test", createHandler(html)); + server.setExecutor(null); + server.start(); + return assignedPort; + } + + private static HttpHandler createHandler(final String html) { + return new HttpHandler() { + @Override + public void handle(final HttpExchange exchange) throws IOException { + exchange.getResponseHeaders().set("Content-Type", "text/html; charset=UTF-8"); + exchange.sendResponseHeaders(200, html.getBytes(StandardCharsets.UTF_8).length); + final OutputStream os = exchange.getResponseBody(); + os.write(html.getBytes(StandardCharsets.UTF_8)); + os.close(); + } + }; + } +} From e621da5fa07f9c77ed7d86051187dce6b8589e82 Mon Sep 17 00:00:00 2001 From: yechop Date: Thu, 26 Sep 2024 15:28:50 +0900 Subject: [PATCH 03/74] =?UTF-8?q?test:=20fakeServer=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../referencelink/fake/FakeServerTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 backend/src/test/java/site/coduo/referencelink/fake/FakeServerTest.java diff --git a/backend/src/test/java/site/coduo/referencelink/fake/FakeServerTest.java b/backend/src/test/java/site/coduo/referencelink/fake/FakeServerTest.java new file mode 100644 index 000000000..ca52af9c8 --- /dev/null +++ b/backend/src/test/java/site/coduo/referencelink/fake/FakeServerTest.java @@ -0,0 +1,21 @@ +package site.coduo.referencelink.fake; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class FakeServerTest { + + @DisplayName("페이크 서버 연결에 성공한다.") + @Test + void connect_success() throws IOException { + final URL url = new URL(FakeServer.testUrl); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + assertThat(connection.getResponseCode()).isEqualTo(200); + } +} From 37fa843f928a3a32026cd3d885fcbd09574ac607 Mon Sep 17 00:00:00 2001 From: yechop Date: Thu, 26 Sep 2024 15:37:04 +0900 Subject: [PATCH 04/74] =?UTF-8?q?test:=20OpenGraphServiceTest=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/OpenGraphServiceTest.java | 124 +++++++++++++----- 1 file changed, 94 insertions(+), 30 deletions(-) diff --git a/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java b/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java index 8117275bc..e649176cf 100644 --- a/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java +++ b/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java @@ -5,10 +5,12 @@ import static site.coduo.fixture.PairRoomFixture.INK_REDDDY_ROOM; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +22,7 @@ import site.coduo.referencelink.domain.Category; import site.coduo.referencelink.domain.OpenGraph; import site.coduo.referencelink.domain.ReferenceLink; +import site.coduo.referencelink.fake.FakeServer; import site.coduo.referencelink.repository.CategoryEntity; import site.coduo.referencelink.repository.CategoryRepository; import site.coduo.referencelink.repository.OpenGraphRepository; @@ -47,6 +50,16 @@ class OpenGraphServiceTest extends CascadeCleaner { @Autowired private CategoryRepository categoryRepository; + private PairRoomEntity pairRoomEntity; + private CategoryEntity category; + + @BeforeEach + void setUp() { + pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); + category = categoryRepository.save( + new CategoryEntity(pairRoomEntity, new Category("스프링"))); + } + @AfterEach void tearDown() { deleteAllPairRoomCascade(); @@ -54,26 +67,22 @@ void tearDown() { @Test @DisplayName("오픈그래프를 생성 후 저장한다.") - void create_open_graph() throws MalformedURLException { + void create_open_graph_exactly() throws IOException { //given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - - final CategoryEntity category = categoryRepository.save( - new CategoryEntity(pairRoomEntity, new Category("스프링"))); - final URL url = new URL("https://www.naver.com"); - final ReferenceLinkEntity referenceLink = new ReferenceLinkEntity( - new ReferenceLink(url, new AccessCode(pairRoomEntity.getAccessCode())), - category, - pairRoomEntity - ); + final URL url = new URL(FakeServer.testUrl); + final ReferenceLinkEntity referenceLink = generateReferenceLinkEntity(url); final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.save(referenceLink); // when - openGraphService.createOpenGraph(referenceLinkEntity, url); + final OpenGraph openGraph = openGraphService.createOpenGraph(referenceLinkEntity, url); - // then - assertThat(openGraphRepository.findAll()) - .hasSize(1); + //then + assertAll( + () -> assertThat(openGraphRepository.findAll()).hasSize(1), + () -> assertThat(openGraph) + .extracting("headTitle", "openGraphTitle", "description", "image") + .contains("헤드 타이틀", "오픈그래프 타이틀", "오픈그래프 설명", "오픈그래프 이미지") + ); } @DisplayName("일치하는 오픈그래프가 없으면 기본 값을 넣은 오픈그래프를 반환한다.") @@ -83,27 +92,18 @@ void return_null_when_cannot_find_open_graph() { final OpenGraph openGraph = openGraphService.findOpenGraph(1L); // then - assertAll( - () -> assertThat(openGraph.getHeadTitle()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE), - () -> assertThat(openGraph.getOpenGraphTitle()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE), - () -> assertThat(openGraph.getDescription()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE), - () -> assertThat(openGraph.getImage()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE) - ); + assertThat(openGraph) + .extracting("headTitle", "openGraphTitle", "description", "image") + .contains(DEFAULT_OPEN_GRAPH_VALUE, DEFAULT_OPEN_GRAPH_VALUE, DEFAULT_OPEN_GRAPH_VALUE, + DEFAULT_OPEN_GRAPH_VALUE); } @DisplayName("레퍼런스링크로 오픈그래프를 삭제한다.") @Test void delete_open_graph_by_reference_link_id() throws MalformedURLException { // given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final CategoryEntity category = categoryRepository.save( - new CategoryEntity(pairRoomEntity, new Category("스프링"))); - final URL url = new URL("https://www.naver.com"); - final ReferenceLinkEntity referenceLink = new ReferenceLinkEntity( - new ReferenceLink(url, new AccessCode(pairRoomEntity.getAccessCode())), - category, - pairRoomEntity - ); + final URL url = new URL(FakeServer.testUrl); + final ReferenceLinkEntity referenceLink = generateReferenceLinkEntity(url); final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.save(referenceLink); openGraphService.createOpenGraph(referenceLinkEntity, url); @@ -113,4 +113,68 @@ void delete_open_graph_by_reference_link_id() throws MalformedURLException { // then assertThat(openGraphRepository.findAll()).isEmpty(); } + + @DisplayName("링크의 도큐먼트를 가져오지 못했을때 헤드타이틀에 도메인을 넣어 생성 후 저장한다.") + @Test + void create_openGraph_when_cannot_get_document() throws IOException { + //given + final int assignedPort = FakeServer.createAndStartFakeServer(null); + final URL url = new URL("http://localhost:" + assignedPort + "/test"); + final ReferenceLinkEntity referenceLink = generateReferenceLinkEntity(url); + final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.save(referenceLink); + + // when + final OpenGraph openGraph = openGraphService.createOpenGraph(referenceLinkEntity, url); + + // then + assertAll( + () -> assertThat(openGraphRepository.findAll()).hasSize(1), + () -> assertThat(openGraph) + .extracting("headTitle", "openGraphTitle", "description", "image") + .contains("localhost", DEFAULT_OPEN_GRAPH_VALUE, DEFAULT_OPEN_GRAPH_VALUE, + DEFAULT_OPEN_GRAPH_VALUE) + ); + } + + @DisplayName("링크의 오픈그래프 타이틀, 헤드타이틀이 없으면 헤드타이틀에 도메인을 넣어 생성 후 저장한다.") + @Test + void create_openGraph_when_titles_are_empty() throws IOException { + //given + final String html = "" + + "" + + "" + + ""; + final int assignedPort = FakeServer.createAndStartFakeServer(html); + + final URL url = new URL("http://localhost:" + assignedPort + "/test"); + final ReferenceLinkEntity referenceLink = generateReferenceLinkEntity(url); + final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.save(referenceLink); + + // when + final OpenGraph openGraph = openGraphService.createOpenGraph(referenceLinkEntity, url); + + // then + assertAll( + () -> assertThat(openGraphRepository.findAll()).hasSize(1), + () -> assertThat(openGraph.getHeadTitle()).isEqualTo("localhost"), + () -> assertThat(openGraph.getOpenGraphTitle()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE), + () -> assertThat(openGraph.getDescription()).isEqualTo("오픈그래프 설명"), + () -> assertThat(openGraph.getImage()).isEqualTo("오픈그래프 이미지") + ); + assertAll( + () -> assertThat(openGraphRepository.findAll()).hasSize(1), + () -> assertThat(openGraph) + .extracting("headTitle", "openGraphTitle", "description", "image") + .contains("localhost", DEFAULT_OPEN_GRAPH_VALUE, "오픈그래프 설명", + "오픈그래프 이미지") + ); + } + + private ReferenceLinkEntity generateReferenceLinkEntity(final URL url) { + return new ReferenceLinkEntity( + new ReferenceLink(url, new AccessCode(pairRoomEntity.getAccessCode())), + category, + pairRoomEntity + ); + } } From 7a5c14a98917e99428937aabb4849785b5a3c11a Mon Sep 17 00:00:00 2001 From: yechop Date: Thu, 26 Sep 2024 15:59:52 +0900 Subject: [PATCH 05/74] =?UTF-8?q?test:=20=EB=A0=88=ED=8D=BC=EB=9F=B0?= =?UTF-8?q?=EC=8A=A4=EB=A7=81=ED=81=AC=EC=99=80=20=EC=98=A4=ED=94=88?= =?UTF-8?q?=EA=B7=B8=EB=9E=98=ED=94=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/CategoryServiceTest.java | 7 +- .../service/ReferenceLinkServiceTest.java | 90 ++++++++++++++----- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/backend/src/test/java/site/coduo/referencelink/service/CategoryServiceTest.java b/backend/src/test/java/site/coduo/referencelink/service/CategoryServiceTest.java index a0bb90c18..770e604e6 100644 --- a/backend/src/test/java/site/coduo/referencelink/service/CategoryServiceTest.java +++ b/backend/src/test/java/site/coduo/referencelink/service/CategoryServiceTest.java @@ -23,6 +23,7 @@ import site.coduo.referencelink.domain.Category; import site.coduo.referencelink.domain.ReferenceLink; import site.coduo.referencelink.exception.InvalidCategoryException; +import site.coduo.referencelink.fake.FakeServer; import site.coduo.referencelink.repository.CategoryEntity; import site.coduo.referencelink.repository.CategoryRepository; import site.coduo.referencelink.repository.ReferenceLinkEntity; @@ -70,7 +71,7 @@ void save_category() { ACCESS_CODE.getValue()); assertThat(categories.stream().anyMatch( category -> category.id().equals(createdCategory.id()) && - category.value().equals(createdCategory.value()))) + category.value().equals(createdCategory.value()))) .isTrue(); } @@ -93,7 +94,7 @@ void update_category() { ACCESS_CODE.getValue()); assertThat(categories.stream().anyMatch( category -> category.id().equals(createdCategory.id()) && - category.value().equals(updatedCategory.updatedCategoryName()))) + category.value().equals(updatedCategory.updatedCategoryName()))) .isTrue(); } @@ -161,7 +162,7 @@ void remove_category_and_update_reference_category_value() throws MalformedURLEx final PairRoomEntity entity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); final CategoryEntity savedCategory = categoryRepository.save(new CategoryEntity(entity, category)); - final ReferenceLink referenceLink = new ReferenceLink(new URL("https://google.com"), ACCESS_CODE); + final ReferenceLink referenceLink = new ReferenceLink(new URL(FakeServer.testUrl), ACCESS_CODE); final ReferenceLinkEntity beforeDeleteCategory = referenceLinkRepository.save( new ReferenceLinkEntity(referenceLink, savedCategory, entity)); diff --git a/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java b/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java index 837af1f31..66bb0f48f 100644 --- a/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java +++ b/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java @@ -1,6 +1,7 @@ package site.coduo.referencelink.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import static site.coduo.fixture.PairRoomFixture.INK_REDDDY_ROOM; @@ -10,6 +11,7 @@ import java.util.List; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +22,8 @@ import site.coduo.pairroom.repository.PairRoomRepository; import site.coduo.referencelink.domain.Category; import site.coduo.referencelink.domain.ReferenceLink; +import site.coduo.referencelink.exception.InvalidUrlFormatException; +import site.coduo.referencelink.fake.FakeServer; import site.coduo.referencelink.repository.CategoryEntity; import site.coduo.referencelink.repository.CategoryRepository; import site.coduo.referencelink.repository.OpenGraphRepository; @@ -47,6 +51,17 @@ class ReferenceLinkServiceTest extends CascadeCleaner { @Autowired private CategoryRepository categoryRepository; + private PairRoomEntity pairRoomEntity; + private CategoryEntity reactCategory; + private CategoryEntity springCategory; + + @BeforeEach + void setUp() { + pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); + reactCategory = categoryRepository.save(new CategoryEntity(pairRoomEntity, new Category("리액트"))); + springCategory = categoryRepository.save(new CategoryEntity(pairRoomEntity, new Category("스프링"))); + } + @AfterEach void tearDown() { deleteAllPairRoomCascade(); @@ -56,8 +71,8 @@ void tearDown() { @DisplayName("레퍼런스 링크와 오픈그래프를 함께 저장한다.") void save_reference_link_and_open_graph() { // given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest("https://www.naver.com", null); + final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest( + FakeServer.testUrl, springCategory.getId()); // when referenceLinkService.createReferenceLink(pairRoomEntity.getAccessCode(), request); @@ -69,34 +84,37 @@ void save_reference_link_and_open_graph() { () -> { final ReferenceLinkResponse referenceLinkResponses = referenceLinkService.readAllReferenceLink(pairRoomEntity.getAccessCode()).get(0); - assertThat(referenceLinkResponses.url()).isEqualTo(request.url()); - assertThat(referenceLinkResponses.headTitle()).isEqualTo("NAVER"); - assertThat(referenceLinkResponses.openGraphTitle()).isEqualTo("네이버"); + assertThat(referenceLinkResponses) + .extracting("url", "headTitle", "openGraphTitle", "description", "image", "categoryName") + .contains(request.url(), "헤드 타이틀", "오픈그래프 타이틀", "오픈그래프 설명", "오픈그래프 이미지", "스프링"); } ); } + @Test + @DisplayName("잘못된 url로 저장을 시도하면 예외가 발생한다.") + void throw_exception_when_invalid_url_format() { + // given + final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest("failUrl", null); + + // when & then + assertThatThrownBy( + () -> referenceLinkService.createReferenceLink(pairRoomEntity.getAccessCode(), request)) + .isInstanceOf(InvalidUrlFormatException.class); + } + @Test @DisplayName("모든 레퍼런스 링크를 조회한다.") void search_all_reference_link() throws MalformedURLException { // given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final CategoryEntity category = categoryRepository.save(new CategoryEntity(pairRoomEntity, new Category("자바"))); final AccessCode accessCode = new AccessCode(pairRoomEntity.getAccessCode()); - referenceLinkRepository.save( - new ReferenceLinkEntity(new ReferenceLink(new URL("http://url1.com"), accessCode), category, - pairRoomEntity)); - referenceLinkRepository.save( - new ReferenceLinkEntity(new ReferenceLink(new URL("http://url2.com"), accessCode), category, - pairRoomEntity)); - referenceLinkRepository.save( - new ReferenceLinkEntity(new ReferenceLink(new URL("http://url3.com"), accessCode), category, - pairRoomEntity)); + referenceLinkRepository.save(generateReferenceLink(springCategory)); + referenceLinkRepository.save(generateReferenceLink(springCategory)); + referenceLinkRepository.save(generateReferenceLink(reactCategory)); // when final List responses = referenceLinkService.readAllReferenceLink( accessCode.getValue()); - // then assertThat(responses).hasSize(3); } @@ -105,13 +123,11 @@ void search_all_reference_link() throws MalformedURLException { @DisplayName("레퍼런스 링크와 오픈그래프를 삭제한다.") void delete_reference_link_and_open_graph() throws MalformedURLException { // given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest("https://www.naver.com", null); - final ReferenceLinkResponse referenceLink = referenceLinkService.createReferenceLink( - pairRoomEntity.getAccessCode(), request); + final ReferenceLinkEntity referenceLink = generateReferenceLink(reactCategory); + final ReferenceLinkEntity saved = referenceLinkRepository.save(referenceLink); // when - referenceLinkService.deleteReferenceLink(pairRoomEntity.getAccessCode(), referenceLink.id()); + referenceLinkService.deleteReferenceLink(pairRoomEntity.getAccessCode(), saved.getId()); // then assertAll( @@ -124,8 +140,8 @@ void delete_reference_link_and_open_graph() throws MalformedURLException { @Test void cannot_delete_reference_link_and_open_graph_when_invalid_access_code() throws MalformedURLException { // given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest("https://www.naver.com", null); + final ReferenceLinkCreateRequest request = + new ReferenceLinkCreateRequest(FakeServer.testUrl, springCategory.getId()); final ReferenceLinkResponse referenceLink = referenceLinkService.createReferenceLink( pairRoomEntity.getAccessCode(), request); @@ -137,4 +153,30 @@ void cannot_delete_reference_link_and_open_graph_when_invalid_access_code() thro () -> assertThat(openGraphRepository.findAll()).hasSize(1) ); } + + @Test + @DisplayName("카테고리가 일치하는 모든 레퍼런스 링크를 조회한다.") + void find_reference_links_by_category() throws MalformedURLException { + // given + final ReferenceLinkEntity reactReferenceLink = generateReferenceLink(reactCategory); + final ReferenceLinkEntity reactReferenceLink2 = generateReferenceLink(reactCategory); + final ReferenceLinkEntity springReferenceLink = generateReferenceLink(springCategory); + + referenceLinkRepository.save(reactReferenceLink); + referenceLinkRepository.save(reactReferenceLink2); + referenceLinkRepository.save(springReferenceLink); + + // when + final List referenceLinksByCategory = referenceLinkService.findReferenceLinksByCategory( + pairRoomEntity.getAccessCode(), reactCategory.getId()); + + // then + assertThat(referenceLinksByCategory).hasSize(2); + } + + private ReferenceLinkEntity generateReferenceLink(final CategoryEntity category) throws MalformedURLException { + return new ReferenceLinkEntity(new ReferenceLink(new URL(FakeServer.testUrl), + new AccessCode(pairRoomEntity.getAccessCode())), + category, pairRoomEntity); + } } From d7f4ee0d748c67198aa718d227f8b63595d18b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Fri, 27 Sep 2024 10:28:59 +0900 Subject: [PATCH 06/74] =?UTF-8?q?[BE]=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=88=98=EC=A0=95=20(#676)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) --- .github/workflows/be_cd-production.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/be_cd-production.yml b/.github/workflows/be_cd-production.yml index dcb4a0829..f5586a8ff 100644 --- a/.github/workflows/be_cd-production.yml +++ b/.github/workflows/be_cd-production.yml @@ -154,7 +154,8 @@ jobs: JWT_KEY=${{ secrets.JWT_KEY}} # INSTANCE NAME - INSTANCE_NAME=${{ secrets.INSTANCE_A_NAME }} + INSTANCE_NAME=${{ secrets.INSTANCE_B_NAME }} + # Server App SERVER_BINDING_PORT=${{ secrets.PRODUCTION_SERVER_BINDING_PORT }} From 1f5af2475e270f4425dc93f9f820a02c0b9e55ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Fri, 27 Sep 2024 10:35:59 +0900 Subject: [PATCH 07/74] Update be_cd-production.yml (#677) --- .github/workflows/be_cd-production.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/be_cd-production.yml b/.github/workflows/be_cd-production.yml index f5586a8ff..717379615 100644 --- a/.github/workflows/be_cd-production.yml +++ b/.github/workflows/be_cd-production.yml @@ -137,16 +137,20 @@ jobs: # Docker Hub info from Github Secrets DOCKER_REPO_NAME=${{ secrets.DOCKER_REPO_NAME }} - # DB Configuration secrets info from Github Secrets + # DB Configuration secrets info from Github Secrets MYSQL_DB_NAME=${{ secrets.MYSQL_DB_NAME }} MYSQL_TIME_ZONE=${{ secrets.MYSQL_TIME_ZONE }} DB_BINDING_PORT=${{ secrets.DB_BINDING_PORT }} DOCKER_DATA_PATH=${{ secrets.DOCKER_DATA_PATH }} - DB_URL=${{ secrets.DB_URL }} - DB_USERNAME=${{ secrets.DB_USERNAME }} - DB_PASSWORD=${{ secrets.DB_PASSWORD }} + MASTER_DB_URL=${{ secrets.MASTER_DB_URL }} + MASTER_DB_USERNAME=${{ secrets.MASTER_DB_USERNAME }} + MASTER_DB_PASSWORD=${{ secrets.MASTER_DB_PASSWORD }} + SLAVE_DB_URL=${{ secrets.SLAVE_DB_URL }} + SLAVE_DB_USERNAME=${{ secrets.SLAVE_DB_USERNAME }} + SLAVE_DB_PASSWORD=${{ secrets.SLAVE_DB_PASSWORD }} DDL_AUTO=${{ secrets.DDL_AUTO }} + # OAUTH & JWT CLIENT_ID=${{ secrets.CLIENT_ID }} CLIENT_SECRET=${{ secrets.CLIENT_SECRET }} From bb8586ef849a870a8533cbf10e31a5ef7cc6c24e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Wed, 2 Oct 2024 16:20:29 +0900 Subject: [PATCH 08/74] =?UTF-8?q?[BE]=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EB=9F=AC=20=EB=A1=9C=EA=B9=85=20=EB=AC=B8=EC=A0=9C=20=EB=B3=B4?= =?UTF-8?q?=EC=99=84=20=20(#679)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) --- .../src/main/java/site/coduo/sync/service/SchedulerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java index 7fe65eaf5..bb77485b5 100644 --- a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java +++ b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java @@ -58,7 +58,7 @@ private void runTimer(final String key, final Timer timer) { timestampRegistry.register(key, initalTimer); return; } - if (sseService.hasNoConnections(key)) { + if (sseService.hasNoConnections(key) && schedulerRegistry.has(key)) { stop(key); return; } From b9f29176fdf026f1b83ffb166832b3df0cf66767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Wed, 2 Oct 2024 16:26:28 +0900 Subject: [PATCH 09/74] =?UTF-8?q?[BE]=20=EC=9A=B4=EC=98=81=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=EB=B0=B0=ED=8F=AC=20=20(#680)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) --- .../src/main/java/site/coduo/sync/service/SchedulerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java index 7fe65eaf5..bb77485b5 100644 --- a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java +++ b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java @@ -58,7 +58,7 @@ private void runTimer(final String key, final Timer timer) { timestampRegistry.register(key, initalTimer); return; } - if (sseService.hasNoConnections(key)) { + if (sseService.hasNoConnections(key) && schedulerRegistry.has(key)) { stop(key); return; } From 039f193048085f5111e50dce92a54a91a19f7280 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Fri, 4 Oct 2024 13:34:48 +0900 Subject: [PATCH 10/74] =?UTF-8?q?style:=20=EB=A1=9C=EA=B7=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coduo/referencelink/controller/ReferenceLinkController.java | 2 -- .../site/coduo/referencelink/service/ReferenceLinkService.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java b/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java index 1f2035495..6145bf32e 100644 --- a/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java +++ b/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java @@ -44,10 +44,8 @@ public ResponseEntity createReferenceLink( public ResponseEntity> getReferenceLinks( @PathVariable("accessCode") final String accessCodeText ) { - log.info("[Reference Link] 1. 링크 조회 API 호출 시작!"); final List responses = referenceLinkService.readAllReferenceLink(accessCodeText); - log.info("[Reference Link] 5. 끝!! 응답!"); return ResponseEntity.ok(responses); } diff --git a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java index fd9f54c96..9fd45f786 100644 --- a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java +++ b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java @@ -68,8 +68,6 @@ private ReferenceLinkEntity saveReferenceLink(final ReferenceLinkCreateRequest r @Transactional(readOnly = true) public List readAllReferenceLink(final String accessCodeText) { - log.info("[Reference Link] 2. readAllReferenceLink 메서드 호출 시작!"); - log.info("[Reference Link] 3. referenceLinkRepository.findAll() 호출 시작!"); final PairRoomEntity pairRoom = pairRoomRepository.fetchByAccessCode(accessCodeText); final List referenceLinkEntities = referenceLinkRepository.findByPairRoomEntity(pairRoom); From 9606f5b87b7df8a00f6594c2c4c350b74fdad7b0 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Fri, 4 Oct 2024 13:40:12 +0900 Subject: [PATCH 11/74] =?UTF-8?q?refactor:=20check=20=EC=8B=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EA=B0=92=20=ED=95=84=EC=88=98=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/site/coduo/member/controller/AuthController.java | 2 +- .../src/main/java/site/coduo/member/service/AuthService.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 287bebc0b..8510d5f78 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -71,7 +71,7 @@ public ResponseEntity signInCallback( @GetMapping("/sign-in/check") public ResponseEntity signInCheck( - @CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken + @CookieValue(name = SIGN_IN_COOKIE_NAME, required = false) final String signInToken ) { final boolean signedIn = authService.isSignedIn(signInToken); final SignInCheckResponse response = new SignInCheckResponse(signedIn); diff --git a/backend/src/main/java/site/coduo/member/service/AuthService.java b/backend/src/main/java/site/coduo/member/service/AuthService.java index ab90dafc6..41878625b 100644 --- a/backend/src/main/java/site/coduo/member/service/AuthService.java +++ b/backend/src/main/java/site/coduo/member/service/AuthService.java @@ -33,6 +33,9 @@ public SignInServiceResponse createSignInToken(final String accessToken) { } public boolean isSignedIn(final String signInToken) { + if (signInToken == null) { + return false; + } return jwtProvider.isValid(signInToken); } } From f97f2c850369b91d9e8421d31eaa84440c826c2d Mon Sep 17 00:00:00 2001 From: yechop Date: Fri, 4 Oct 2024 14:18:37 +0900 Subject: [PATCH 12/74] =?UTF-8?q?refactor:=20=ED=8E=98=EC=96=B4=EB=A3=B8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=A0=88=ED=8D=BC=EB=9F=B0=EC=8A=A4?= =?UTF-8?q?=EB=A7=81=ED=81=AC=20=EC=B0=BE=EB=8A=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../referencelink/repository/ReferenceLinkRepository.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java b/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java index 802be26de..9721e5a7c 100644 --- a/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java +++ b/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java @@ -4,12 +4,15 @@ import org.springframework.data.jpa.repository.JpaRepository; +import site.coduo.pairroom.repository.PairRoomEntity; import site.coduo.referencelink.exception.ReferenceLinkException; public interface ReferenceLinkRepository extends JpaRepository { + List findByPairRoomEntity(PairRoomEntity pairRoomEntity); + default ReferenceLinkEntity fetchById(long id) { return findById(id) - .orElseThrow(() -> new ReferenceLinkException("존재하지 않는 링크입니다..")); + .orElseThrow(() -> new ReferenceLinkException("존재하지 않는 링크입니다.")); } } From cc98d77ec4dcb9d34636d59256f3c3855fbca580 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 5 Oct 2024 16:39:51 +0900 Subject: [PATCH 13/74] =?UTF-8?q?style:=20=EB=A1=9C=EA=B7=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/site/coduo/pairroom/service/PairRoomService.java | 4 ---- .../coduo/referencelink/service/ReferenceLinkService.java | 1 - 2 files changed, 5 deletions(-) diff --git a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java index 09c22b3c8..54415cdaf 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java +++ b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java @@ -43,9 +43,6 @@ public class PairRoomService { @Transactional public String savePairRoom(final PairRoomCreateRequest request, @Nullable final String token) { final PairRoom pairRoom = createPairRoom(request); - final PairRoomEntity entity = PairRoomEntity.from(pairRoom); - log.info("Pair ROom entity : {}", entity); - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(pairRoom)); final Timer timer = new Timer(pairRoom.getAccessCode(), request.timerDuration(), request.timerRemainingTime()); @@ -67,7 +64,6 @@ private PairRoom createPairRoom(final PairRoomCreateRequest request) { private AccessCode generateAccessCode() { final String generatedAccessCode = uuidAccessCodeGenerator.generate(); - log.info("ACCESS CODE : {}", generatedAccessCode); if (pairRoomRepository.existsByAccessCode(generatedAccessCode)) { return generateAccessCode(); } diff --git a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java index 9fd45f786..e22fb208d 100644 --- a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java +++ b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java @@ -72,7 +72,6 @@ public List readAllReferenceLink(final String accessCodeT final List referenceLinkEntities = referenceLinkRepository.findByPairRoomEntity(pairRoom); - log.info("[Reference Link] 4. referenceLinkRepository.findAll() 반환 데이터 필터링 시작!!"); return referenceLinkEntities.stream() .map(this::makeReferenceLinkResponse) .toList(); From 4e0dc372f102774317b0bd64a29fdbdcbad2a59e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Sat, 5 Oct 2024 16:56:33 +0900 Subject: [PATCH 14/74] =?UTF-8?q?[BE]=20TEST=20DB=20=EC=86=8C=EC=8A=A4=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#686)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/be_cd-test.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/be_cd-test.yml b/.github/workflows/be_cd-test.yml index 9a08d05f1..0eec28b85 100644 --- a/.github/workflows/be_cd-test.yml +++ b/.github/workflows/be_cd-test.yml @@ -75,13 +75,13 @@ jobs: DOCKER_REPO_NAME=${{ secrets.DOCKER_REPO_NAME }} # DB Configuration secrets info from Github Secrets - MASTER_DB_URL=${{ secrets.MASTER_DB_URL }} - MASTER_DB_USERNAME=${{ secrets.MASTER_DB_USERNAME }} - MASTER_DB_PASSWORD=${{ secrets.MASTER_DB_PASSWORD }} + MASTER_DB_URL=${{ secrets.TEST_SERVER_DB_URL }} + MASTER_DB_USERNAME=${{ secrets.TEST_SERVER_DB_USERNAME }} + MASTER_DB_PASSWORD=${{ secrets.TEST_SERVER_DB_PASSWORD }} - SLAVE_DB_URL=${{ secrets.SLAVE_DB_URL }} - SLAVE_DB_USERNAME=${{ secrets.SLAVE_DB_USERNAME }} - SLAVE_DB_PASSWORD=${{ secrets.SLAVE_DB_PASSWORD }} + SLAVE_DB_URL=${{ secrets.TEST_SERVER_DB_URL }} + SLAVE_DB_USERNAME=${{ secrets.TEST_SERVER_DB_USERNAME }} + SLAVE_DB_PASSWORD=${{ secrets.TEST_SERVER_DB_PASSWORD }} DDL_AUTO=${{ secrets.DDL_AUTO }} # OAUTH & JWT From 871f7677eb74951de5a7c7151a60c8cfe158d103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Sat, 5 Oct 2024 16:59:00 +0900 Subject: [PATCH 15/74] =?UTF-8?q?[BE]=20TEST=20DB=20=EC=86=8C=EC=8A=A4=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#686)=20(#687)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/be_cd-test.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/be_cd-test.yml b/.github/workflows/be_cd-test.yml index 9a08d05f1..0eec28b85 100644 --- a/.github/workflows/be_cd-test.yml +++ b/.github/workflows/be_cd-test.yml @@ -75,13 +75,13 @@ jobs: DOCKER_REPO_NAME=${{ secrets.DOCKER_REPO_NAME }} # DB Configuration secrets info from Github Secrets - MASTER_DB_URL=${{ secrets.MASTER_DB_URL }} - MASTER_DB_USERNAME=${{ secrets.MASTER_DB_USERNAME }} - MASTER_DB_PASSWORD=${{ secrets.MASTER_DB_PASSWORD }} + MASTER_DB_URL=${{ secrets.TEST_SERVER_DB_URL }} + MASTER_DB_USERNAME=${{ secrets.TEST_SERVER_DB_USERNAME }} + MASTER_DB_PASSWORD=${{ secrets.TEST_SERVER_DB_PASSWORD }} - SLAVE_DB_URL=${{ secrets.SLAVE_DB_URL }} - SLAVE_DB_USERNAME=${{ secrets.SLAVE_DB_USERNAME }} - SLAVE_DB_PASSWORD=${{ secrets.SLAVE_DB_PASSWORD }} + SLAVE_DB_URL=${{ secrets.TEST_SERVER_DB_URL }} + SLAVE_DB_USERNAME=${{ secrets.TEST_SERVER_DB_USERNAME }} + SLAVE_DB_PASSWORD=${{ secrets.TEST_SERVER_DB_PASSWORD }} DDL_AUTO=${{ secrets.DDL_AUTO }} # OAUTH & JWT From a71d34e73abbc00761659cda840952e2ab004297 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 5 Oct 2024 17:35:46 +0900 Subject: [PATCH 16/74] =?UTF-8?q?refactor:=20stop=20=EB=B0=8F=20pause=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=84=B0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/site/coduo/sync/service/SchedulerService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java index bb77485b5..cb414e730 100644 --- a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java +++ b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java @@ -53,13 +53,11 @@ private void scheduling(final String key, final Timer timer) { private void runTimer(final String key, final Timer timer) { if (timer.isTimeUp()) { - stop(key); - final Timer initalTimer = new Timer(timer.getAccessCode(), timer.getDuration(), timer.getDuration()); - timestampRegistry.register(key, initalTimer); + stop(key, timer); return; } if (sseService.hasNoConnections(key) && schedulerRegistry.has(key)) { - stop(key); + pause(key); return; } timer.decreaseRemainingTime(DELAY_SECOND.toMillis()); @@ -71,8 +69,10 @@ public void pause(final String key) { schedulerRegistry.release(key); } - public void stop(final String key) { + private void stop(final String key, final Timer timer) { sseService.broadcast(key, "timer", "stop"); schedulerRegistry.release(key); + final Timer initalTimer = new Timer(timer.getAccessCode(), timer.getDuration(), timer.getDuration()); + timestampRegistry.register(key, initalTimer); } } From 014e45681496a86f6aa6085fcdfb68fee2176880 Mon Sep 17 00:00:00 2001 From: lemone Date: Sat, 5 Oct 2024 17:45:59 +0900 Subject: [PATCH 17/74] =?UTF-8?q?feat:=20=ED=83=80=EC=9D=B4=EB=A8=B8?= =?UTF-8?q?=EA=B0=80=20=EC=8B=A4=ED=96=89=20=EC=A4=91=EC=97=90=20=EB=8B=A4?= =?UTF-8?q?=EC=8B=9C=20=EC=8B=A4=ED=96=89=EB=90=A0=20=EC=88=98=20=EC=97=86?= =?UTF-8?q?=EA=B2=8C=20=EC=A1=B0=EA=B1=B4=EB=AC=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/site/coduo/sync/service/SchedulerService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java index cb414e730..53f1b72fb 100644 --- a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java +++ b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java @@ -29,6 +29,9 @@ public class SchedulerService { private final SseService sseService; public void start(final String key) { + if (schedulerRegistry.isActive(key)) { + return; + } sseService.broadcast(key, "timer", "start"); if (isInitial(key)) { final Timer timer = timerRepository.fetchTimerByAccessCode(key) @@ -52,7 +55,7 @@ private void scheduling(final String key, final Timer timer) { } private void runTimer(final String key, final Timer timer) { - if (timer.isTimeUp()) { + if (timer.isTimeUp() && schedulerRegistry.has(key)) { stop(key, timer); return; } From 7c68df1e67941aef820d68d84299ce1940a6e2ec Mon Sep 17 00:00:00 2001 From: lemone Date: Sat, 5 Oct 2024 17:55:36 +0900 Subject: [PATCH 18/74] =?UTF-8?q?feat:=20=ED=83=80=EC=9D=B4=EB=A8=B8?= =?UTF-8?q?=EA=B0=80=20=EC=8B=A4=ED=96=89=20=EC=A4=91=EC=9D=BC=20=EB=95=8C?= =?UTF-8?q?=EB=A7=8C=20=EC=A2=85=EB=A3=8C=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EA=B2=8C=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/site/coduo/sync/service/SchedulerService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java index 53f1b72fb..8cc4795f9 100644 --- a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java +++ b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java @@ -68,8 +68,10 @@ private void runTimer(final String key, final Timer timer) { } public void pause(final String key) { - sseService.broadcast(key, "timer", "pause"); - schedulerRegistry.release(key); + if (schedulerRegistry.isActive(key)) { + sseService.broadcast(key, "timer", "pause"); + schedulerRegistry.release(key); + } } private void stop(final String key, final Timer timer) { From d5e5644492e3e79bbb14792df7157f62b5c38d02 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 5 Oct 2024 18:19:57 +0900 Subject: [PATCH 19/74] =?UTF-8?q?refactor:=20TimerEntity=20=EC=B0=BE?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C=20pairRoom=20id=20=EB=8C=80=EC=8B=A0=20Pa?= =?UTF-8?q?irRoomEntity=EB=A1=9C=20=EB=84=98=EA=B8=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/site/coduo/pairroom/service/PairRoomService.java | 2 +- .../java/site/coduo/timer/repository/TimerRepository.java | 7 ++++--- .../main/java/site/coduo/timer/service/TimerService.java | 4 ++-- .../site/coduo/timer/repository/TimerRepositoryTest.java | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java index 54415cdaf..1f6c0ec00 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java +++ b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java @@ -85,7 +85,7 @@ public void updatePairRoomStatus(final String accessCode, final String statusNam public PairRoomReadResponse findPairRoomAndTimer(final String accessCode) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); - final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomId(pairRoomEntity.getId()); + final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); return PairRoomReadResponse.of(pairRoomEntity.toDomain(), timerEntity.toDomain()); } diff --git a/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java b/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java index 0e7f704c8..12f7cf145 100644 --- a/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java +++ b/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java @@ -4,16 +4,17 @@ import org.springframework.data.jpa.repository.JpaRepository; +import site.coduo.pairroom.repository.PairRoomEntity; import site.coduo.timer.exception.TimerNotFoundException; public interface TimerRepository extends JpaRepository { - default TimerEntity fetchTimerByPairRoomId(final long pairRoomId) { - return findByPairRoomEntityId(pairRoomId) + default TimerEntity fetchTimerByPairRoomEntity(final PairRoomEntity pairRoomEntity) { + return findByPairRoomEntity(pairRoomEntity) .orElseThrow(() -> new TimerNotFoundException("해당 페어룸의 타이머가 존재하지 않습니다.")); } - Optional findByPairRoomEntityId(long pairRoomId); + Optional findByPairRoomEntity(PairRoomEntity pairRoomEntity); default TimerEntity fetchTimerByAccessCode(final String accessCode) { return findByPairRoomEntityAccessCode(accessCode) diff --git a/backend/src/main/java/site/coduo/timer/service/TimerService.java b/backend/src/main/java/site/coduo/timer/service/TimerService.java index c594b6712..dc22962b3 100644 --- a/backend/src/main/java/site/coduo/timer/service/TimerService.java +++ b/backend/src/main/java/site/coduo/timer/service/TimerService.java @@ -24,7 +24,7 @@ public class TimerService { public TimerReadResponse readTimer(final String accessCode) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); - final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomId(pairRoomEntity.getId()); + final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); return TimerReadResponse.of(timerEntity.getId(), timerEntity.toDomain()); } @@ -41,7 +41,7 @@ public long readTimerRemainingTime(final String accessCode) { @Transactional public void updateTimer(final String accessCode, final TimerUpdateRequest updateRequest) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); - final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomId(pairRoomEntity.getId()); + final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); final Timer newTimer = new Timer( new AccessCode(pairRoomEntity.getAccessCode()), updateRequest.duration(), diff --git a/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java b/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java index 63732874d..29acaa9b8 100644 --- a/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java +++ b/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java @@ -51,7 +51,7 @@ void inquiry_timer() { // when final TimerEntity actual = timerRepository - .fetchTimerByPairRoomId(entity.getId()); + .fetchTimerByPairRoomEntity(entity); // then assertThat(actual) From 6df1bb82d2099dc79cadf480f526aa9b0d7e2b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Sat, 5 Oct 2024 18:32:25 +0900 Subject: [PATCH 20/74] =?UTF-8?q?[BE]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EB=B0=B0=ED=8F=AC=20(#689)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --- .../pairroom/service/PairRoomService.java | 6 +----- .../service/ReferenceLinkService.java | 1 - .../coduo/sync/service/SchedulerService.java | 21 ++++++++++++------- .../timer/repository/TimerRepository.java | 7 ++++--- .../coduo/timer/service/TimerService.java | 4 ++-- .../timer/repository/TimerRepositoryTest.java | 2 +- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java index 09c22b3c8..1f6c0ec00 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java +++ b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java @@ -43,9 +43,6 @@ public class PairRoomService { @Transactional public String savePairRoom(final PairRoomCreateRequest request, @Nullable final String token) { final PairRoom pairRoom = createPairRoom(request); - final PairRoomEntity entity = PairRoomEntity.from(pairRoom); - log.info("Pair ROom entity : {}", entity); - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(pairRoom)); final Timer timer = new Timer(pairRoom.getAccessCode(), request.timerDuration(), request.timerRemainingTime()); @@ -67,7 +64,6 @@ private PairRoom createPairRoom(final PairRoomCreateRequest request) { private AccessCode generateAccessCode() { final String generatedAccessCode = uuidAccessCodeGenerator.generate(); - log.info("ACCESS CODE : {}", generatedAccessCode); if (pairRoomRepository.existsByAccessCode(generatedAccessCode)) { return generateAccessCode(); } @@ -89,7 +85,7 @@ public void updatePairRoomStatus(final String accessCode, final String statusNam public PairRoomReadResponse findPairRoomAndTimer(final String accessCode) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); - final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomId(pairRoomEntity.getId()); + final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); return PairRoomReadResponse.of(pairRoomEntity.toDomain(), timerEntity.toDomain()); } diff --git a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java index 9fd45f786..e22fb208d 100644 --- a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java +++ b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java @@ -72,7 +72,6 @@ public List readAllReferenceLink(final String accessCodeT final List referenceLinkEntities = referenceLinkRepository.findByPairRoomEntity(pairRoom); - log.info("[Reference Link] 4. referenceLinkRepository.findAll() 반환 데이터 필터링 시작!!"); return referenceLinkEntities.stream() .map(this::makeReferenceLinkResponse) .toList(); diff --git a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java index bb77485b5..8cc4795f9 100644 --- a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java +++ b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java @@ -29,6 +29,9 @@ public class SchedulerService { private final SseService sseService; public void start(final String key) { + if (schedulerRegistry.isActive(key)) { + return; + } sseService.broadcast(key, "timer", "start"); if (isInitial(key)) { final Timer timer = timerRepository.fetchTimerByAccessCode(key) @@ -52,14 +55,12 @@ private void scheduling(final String key, final Timer timer) { } private void runTimer(final String key, final Timer timer) { - if (timer.isTimeUp()) { - stop(key); - final Timer initalTimer = new Timer(timer.getAccessCode(), timer.getDuration(), timer.getDuration()); - timestampRegistry.register(key, initalTimer); + if (timer.isTimeUp() && schedulerRegistry.has(key)) { + stop(key, timer); return; } if (sseService.hasNoConnections(key) && schedulerRegistry.has(key)) { - stop(key); + pause(key); return; } timer.decreaseRemainingTime(DELAY_SECOND.toMillis()); @@ -67,12 +68,16 @@ private void runTimer(final String key, final Timer timer) { } public void pause(final String key) { - sseService.broadcast(key, "timer", "pause"); - schedulerRegistry.release(key); + if (schedulerRegistry.isActive(key)) { + sseService.broadcast(key, "timer", "pause"); + schedulerRegistry.release(key); + } } - public void stop(final String key) { + private void stop(final String key, final Timer timer) { sseService.broadcast(key, "timer", "stop"); schedulerRegistry.release(key); + final Timer initalTimer = new Timer(timer.getAccessCode(), timer.getDuration(), timer.getDuration()); + timestampRegistry.register(key, initalTimer); } } diff --git a/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java b/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java index 0e7f704c8..12f7cf145 100644 --- a/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java +++ b/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java @@ -4,16 +4,17 @@ import org.springframework.data.jpa.repository.JpaRepository; +import site.coduo.pairroom.repository.PairRoomEntity; import site.coduo.timer.exception.TimerNotFoundException; public interface TimerRepository extends JpaRepository { - default TimerEntity fetchTimerByPairRoomId(final long pairRoomId) { - return findByPairRoomEntityId(pairRoomId) + default TimerEntity fetchTimerByPairRoomEntity(final PairRoomEntity pairRoomEntity) { + return findByPairRoomEntity(pairRoomEntity) .orElseThrow(() -> new TimerNotFoundException("해당 페어룸의 타이머가 존재하지 않습니다.")); } - Optional findByPairRoomEntityId(long pairRoomId); + Optional findByPairRoomEntity(PairRoomEntity pairRoomEntity); default TimerEntity fetchTimerByAccessCode(final String accessCode) { return findByPairRoomEntityAccessCode(accessCode) diff --git a/backend/src/main/java/site/coduo/timer/service/TimerService.java b/backend/src/main/java/site/coduo/timer/service/TimerService.java index c594b6712..dc22962b3 100644 --- a/backend/src/main/java/site/coduo/timer/service/TimerService.java +++ b/backend/src/main/java/site/coduo/timer/service/TimerService.java @@ -24,7 +24,7 @@ public class TimerService { public TimerReadResponse readTimer(final String accessCode) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); - final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomId(pairRoomEntity.getId()); + final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); return TimerReadResponse.of(timerEntity.getId(), timerEntity.toDomain()); } @@ -41,7 +41,7 @@ public long readTimerRemainingTime(final String accessCode) { @Transactional public void updateTimer(final String accessCode, final TimerUpdateRequest updateRequest) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); - final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomId(pairRoomEntity.getId()); + final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); final Timer newTimer = new Timer( new AccessCode(pairRoomEntity.getAccessCode()), updateRequest.duration(), diff --git a/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java b/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java index 63732874d..29acaa9b8 100644 --- a/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java +++ b/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java @@ -51,7 +51,7 @@ void inquiry_timer() { // when final TimerEntity actual = timerRepository - .fetchTimerByPairRoomId(entity.getId()); + .fetchTimerByPairRoomEntity(entity); // then assertThat(actual) From 08c490921e398c4f9a4bc34c00170190ae583f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Sat, 5 Oct 2024 18:37:32 +0900 Subject: [PATCH 21/74] =?UTF-8?q?[BE]=20=EC=9A=B4=EC=98=81=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=EB=B0=B0=ED=8F=AC=20=20(#690)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --- .github/workflows/be_cd-test.yml | 12 +++++------ .../member/controller/AuthController.java | 2 +- .../coduo/member/service/AuthService.java | 3 +++ .../pairroom/service/PairRoomService.java | 6 +----- .../controller/ReferenceLinkController.java | 2 -- .../service/ReferenceLinkService.java | 3 --- .../coduo/sync/service/SchedulerService.java | 21 ++++++++++++------- .../timer/repository/TimerRepository.java | 7 ++++--- .../coduo/timer/service/TimerService.java | 4 ++-- .../timer/repository/TimerRepositoryTest.java | 2 +- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/be_cd-test.yml b/.github/workflows/be_cd-test.yml index 9a08d05f1..0eec28b85 100644 --- a/.github/workflows/be_cd-test.yml +++ b/.github/workflows/be_cd-test.yml @@ -75,13 +75,13 @@ jobs: DOCKER_REPO_NAME=${{ secrets.DOCKER_REPO_NAME }} # DB Configuration secrets info from Github Secrets - MASTER_DB_URL=${{ secrets.MASTER_DB_URL }} - MASTER_DB_USERNAME=${{ secrets.MASTER_DB_USERNAME }} - MASTER_DB_PASSWORD=${{ secrets.MASTER_DB_PASSWORD }} + MASTER_DB_URL=${{ secrets.TEST_SERVER_DB_URL }} + MASTER_DB_USERNAME=${{ secrets.TEST_SERVER_DB_USERNAME }} + MASTER_DB_PASSWORD=${{ secrets.TEST_SERVER_DB_PASSWORD }} - SLAVE_DB_URL=${{ secrets.SLAVE_DB_URL }} - SLAVE_DB_USERNAME=${{ secrets.SLAVE_DB_USERNAME }} - SLAVE_DB_PASSWORD=${{ secrets.SLAVE_DB_PASSWORD }} + SLAVE_DB_URL=${{ secrets.TEST_SERVER_DB_URL }} + SLAVE_DB_USERNAME=${{ secrets.TEST_SERVER_DB_USERNAME }} + SLAVE_DB_PASSWORD=${{ secrets.TEST_SERVER_DB_PASSWORD }} DDL_AUTO=${{ secrets.DDL_AUTO }} # OAUTH & JWT diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 287bebc0b..8510d5f78 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -71,7 +71,7 @@ public ResponseEntity signInCallback( @GetMapping("/sign-in/check") public ResponseEntity signInCheck( - @CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken + @CookieValue(name = SIGN_IN_COOKIE_NAME, required = false) final String signInToken ) { final boolean signedIn = authService.isSignedIn(signInToken); final SignInCheckResponse response = new SignInCheckResponse(signedIn); diff --git a/backend/src/main/java/site/coduo/member/service/AuthService.java b/backend/src/main/java/site/coduo/member/service/AuthService.java index ab90dafc6..41878625b 100644 --- a/backend/src/main/java/site/coduo/member/service/AuthService.java +++ b/backend/src/main/java/site/coduo/member/service/AuthService.java @@ -33,6 +33,9 @@ public SignInServiceResponse createSignInToken(final String accessToken) { } public boolean isSignedIn(final String signInToken) { + if (signInToken == null) { + return false; + } return jwtProvider.isValid(signInToken); } } diff --git a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java index 09c22b3c8..1f6c0ec00 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java +++ b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java @@ -43,9 +43,6 @@ public class PairRoomService { @Transactional public String savePairRoom(final PairRoomCreateRequest request, @Nullable final String token) { final PairRoom pairRoom = createPairRoom(request); - final PairRoomEntity entity = PairRoomEntity.from(pairRoom); - log.info("Pair ROom entity : {}", entity); - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(pairRoom)); final Timer timer = new Timer(pairRoom.getAccessCode(), request.timerDuration(), request.timerRemainingTime()); @@ -67,7 +64,6 @@ private PairRoom createPairRoom(final PairRoomCreateRequest request) { private AccessCode generateAccessCode() { final String generatedAccessCode = uuidAccessCodeGenerator.generate(); - log.info("ACCESS CODE : {}", generatedAccessCode); if (pairRoomRepository.existsByAccessCode(generatedAccessCode)) { return generateAccessCode(); } @@ -89,7 +85,7 @@ public void updatePairRoomStatus(final String accessCode, final String statusNam public PairRoomReadResponse findPairRoomAndTimer(final String accessCode) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); - final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomId(pairRoomEntity.getId()); + final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); return PairRoomReadResponse.of(pairRoomEntity.toDomain(), timerEntity.toDomain()); } diff --git a/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java b/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java index 1f2035495..6145bf32e 100644 --- a/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java +++ b/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java @@ -44,10 +44,8 @@ public ResponseEntity createReferenceLink( public ResponseEntity> getReferenceLinks( @PathVariable("accessCode") final String accessCodeText ) { - log.info("[Reference Link] 1. 링크 조회 API 호출 시작!"); final List responses = referenceLinkService.readAllReferenceLink(accessCodeText); - log.info("[Reference Link] 5. 끝!! 응답!"); return ResponseEntity.ok(responses); } diff --git a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java index fd9f54c96..e22fb208d 100644 --- a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java +++ b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java @@ -68,13 +68,10 @@ private ReferenceLinkEntity saveReferenceLink(final ReferenceLinkCreateRequest r @Transactional(readOnly = true) public List readAllReferenceLink(final String accessCodeText) { - log.info("[Reference Link] 2. readAllReferenceLink 메서드 호출 시작!"); - log.info("[Reference Link] 3. referenceLinkRepository.findAll() 호출 시작!"); final PairRoomEntity pairRoom = pairRoomRepository.fetchByAccessCode(accessCodeText); final List referenceLinkEntities = referenceLinkRepository.findByPairRoomEntity(pairRoom); - log.info("[Reference Link] 4. referenceLinkRepository.findAll() 반환 데이터 필터링 시작!!"); return referenceLinkEntities.stream() .map(this::makeReferenceLinkResponse) .toList(); diff --git a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java index bb77485b5..8cc4795f9 100644 --- a/backend/src/main/java/site/coduo/sync/service/SchedulerService.java +++ b/backend/src/main/java/site/coduo/sync/service/SchedulerService.java @@ -29,6 +29,9 @@ public class SchedulerService { private final SseService sseService; public void start(final String key) { + if (schedulerRegistry.isActive(key)) { + return; + } sseService.broadcast(key, "timer", "start"); if (isInitial(key)) { final Timer timer = timerRepository.fetchTimerByAccessCode(key) @@ -52,14 +55,12 @@ private void scheduling(final String key, final Timer timer) { } private void runTimer(final String key, final Timer timer) { - if (timer.isTimeUp()) { - stop(key); - final Timer initalTimer = new Timer(timer.getAccessCode(), timer.getDuration(), timer.getDuration()); - timestampRegistry.register(key, initalTimer); + if (timer.isTimeUp() && schedulerRegistry.has(key)) { + stop(key, timer); return; } if (sseService.hasNoConnections(key) && schedulerRegistry.has(key)) { - stop(key); + pause(key); return; } timer.decreaseRemainingTime(DELAY_SECOND.toMillis()); @@ -67,12 +68,16 @@ private void runTimer(final String key, final Timer timer) { } public void pause(final String key) { - sseService.broadcast(key, "timer", "pause"); - schedulerRegistry.release(key); + if (schedulerRegistry.isActive(key)) { + sseService.broadcast(key, "timer", "pause"); + schedulerRegistry.release(key); + } } - public void stop(final String key) { + private void stop(final String key, final Timer timer) { sseService.broadcast(key, "timer", "stop"); schedulerRegistry.release(key); + final Timer initalTimer = new Timer(timer.getAccessCode(), timer.getDuration(), timer.getDuration()); + timestampRegistry.register(key, initalTimer); } } diff --git a/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java b/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java index 0e7f704c8..12f7cf145 100644 --- a/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java +++ b/backend/src/main/java/site/coduo/timer/repository/TimerRepository.java @@ -4,16 +4,17 @@ import org.springframework.data.jpa.repository.JpaRepository; +import site.coduo.pairroom.repository.PairRoomEntity; import site.coduo.timer.exception.TimerNotFoundException; public interface TimerRepository extends JpaRepository { - default TimerEntity fetchTimerByPairRoomId(final long pairRoomId) { - return findByPairRoomEntityId(pairRoomId) + default TimerEntity fetchTimerByPairRoomEntity(final PairRoomEntity pairRoomEntity) { + return findByPairRoomEntity(pairRoomEntity) .orElseThrow(() -> new TimerNotFoundException("해당 페어룸의 타이머가 존재하지 않습니다.")); } - Optional findByPairRoomEntityId(long pairRoomId); + Optional findByPairRoomEntity(PairRoomEntity pairRoomEntity); default TimerEntity fetchTimerByAccessCode(final String accessCode) { return findByPairRoomEntityAccessCode(accessCode) diff --git a/backend/src/main/java/site/coduo/timer/service/TimerService.java b/backend/src/main/java/site/coduo/timer/service/TimerService.java index c594b6712..dc22962b3 100644 --- a/backend/src/main/java/site/coduo/timer/service/TimerService.java +++ b/backend/src/main/java/site/coduo/timer/service/TimerService.java @@ -24,7 +24,7 @@ public class TimerService { public TimerReadResponse readTimer(final String accessCode) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); - final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomId(pairRoomEntity.getId()); + final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); return TimerReadResponse.of(timerEntity.getId(), timerEntity.toDomain()); } @@ -41,7 +41,7 @@ public long readTimerRemainingTime(final String accessCode) { @Transactional public void updateTimer(final String accessCode, final TimerUpdateRequest updateRequest) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); - final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomId(pairRoomEntity.getId()); + final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); final Timer newTimer = new Timer( new AccessCode(pairRoomEntity.getAccessCode()), updateRequest.duration(), diff --git a/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java b/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java index 63732874d..29acaa9b8 100644 --- a/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java +++ b/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java @@ -51,7 +51,7 @@ void inquiry_timer() { // when final TimerEntity actual = timerRepository - .fetchTimerByPairRoomId(entity.getId()); + .fetchTimerByPairRoomEntity(entity); // then assertThat(actual) From 7ef4231b65d0f9369821f3da50b421fae2c1b94a Mon Sep 17 00:00:00 2001 From: greetings1012 Date: Mon, 7 Oct 2024 15:01:26 +0900 Subject: [PATCH 22/74] =?UTF-8?q?=F0=9F=9A=91=EF=B8=8F=20=EC=98=A4?= =?UTF-8?q?=ED=94=84=EB=9D=BC=EC=9D=B8=20=ED=96=89=EC=82=AC=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=9D=84=20=EC=9C=84=ED=95=B4=20=EC=9D=BC=EC=8B=9C?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=ED=8E=98=EC=96=B4=EB=A3=B8=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=B2=84=ED=8A=BC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/PairRoom/PairListCard/PairListCard.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/PairRoom/PairListCard/PairListCard.tsx b/frontend/src/components/PairRoom/PairListCard/PairListCard.tsx index ad0a9acdf..68f02478e 100644 --- a/frontend/src/components/PairRoom/PairListCard/PairListCard.tsx +++ b/frontend/src/components/PairRoom/PairListCard/PairListCard.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; -import DeleteButton from '@/components/PairRoom/PairListCard/DeleteButton/DeleteButton'; +// import DeleteButton from '@/components/PairRoom/PairListCard/DeleteButton/DeleteButton'; import Header from '@/components/PairRoom/PairListCard/Header/Header'; import PairListSection from '@/components/PairRoom/PairListCard/PairListSection/PairListSection'; import RoomCodeSection from '@/components/PairRoom/PairListCard/RoomCodeSection/RoomCodeSection'; @@ -12,10 +12,10 @@ interface PairListCardProps { driver: string; navigator: string; roomCode: string; - onRoomDelete: () => void; + onRoomDelete?: () => void; } -const PairListCard = ({ driver, navigator, roomCode, onRoomDelete }: PairListCardProps) => { +const PairListCard = ({ driver, navigator, roomCode }: PairListCardProps) => { const [isOpen, setIsOpen] = useState(true); const toggleOpen = () => setIsOpen(!isOpen); @@ -27,7 +27,7 @@ const PairListCard = ({ driver, navigator, roomCode, onRoomDelete }: PairListCar - + {/* */} From 8473869bbce9aeb32162ff632bacac1872f89bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 15:14:04 +0900 Subject: [PATCH 23/74] =?UTF-8?q?[BE]=20=ED=81=B4=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EC=96=B8=ED=8A=B8=20=EC=97=90=EB=9F=AC=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=97=86=EC=95=A0=EA=B8=B0=20(#692)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --- .../main/java/site/coduo/common/config/web/FilterConfig.java | 2 +- .../src/main/java/site/coduo/sync/service/SseEventStream.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java index 465cfd246..486e2801f 100644 --- a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java @@ -39,7 +39,7 @@ public FilterRegistrationBean accessTokenSessionFilter public FilterRegistrationBean signInCookieFilter(final JwtProvider jwtProvider) { final FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setFilter(new SignInCookieFilter(jwtProvider)); - bean.addUrlPatterns("/api/sign-out", "/api/member", "/api/sign-in/check"); + bean.addUrlPatterns("/api/sign-out", "/api/member"); bean.setOrder(1); return bean; } diff --git a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java index 6a4bab98c..b764ffcb6 100644 --- a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java +++ b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java @@ -59,7 +59,7 @@ public void flush(final String name, final String message) { .name(name) .data(message) ); - } catch (IOException e) { + } catch (final IOException e) { log.warn("SSE 통신 중 에러가 발생했습니다."); } } From e8cb1fe749860021793b5223f690ebb51dadc984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 15:18:20 +0900 Subject: [PATCH 24/74] =?UTF-8?q?[BE]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EB=B0=B0=ED=8F=AC=20(#693)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 * [BE] 클라이언트 에러 로그 없애기 (#692) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --- .../main/java/site/coduo/common/config/web/FilterConfig.java | 2 +- .../src/main/java/site/coduo/sync/service/SseEventStream.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java index 465cfd246..486e2801f 100644 --- a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java @@ -39,7 +39,7 @@ public FilterRegistrationBean accessTokenSessionFilter public FilterRegistrationBean signInCookieFilter(final JwtProvider jwtProvider) { final FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setFilter(new SignInCookieFilter(jwtProvider)); - bean.addUrlPatterns("/api/sign-out", "/api/member", "/api/sign-in/check"); + bean.addUrlPatterns("/api/sign-out", "/api/member"); bean.setOrder(1); return bean; } diff --git a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java index 6a4bab98c..b764ffcb6 100644 --- a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java +++ b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java @@ -59,7 +59,7 @@ public void flush(final String name, final String message) { .name(name) .data(message) ); - } catch (IOException e) { + } catch (final IOException e) { log.warn("SSE 통신 중 에러가 발생했습니다."); } } From f433138be3c819567e7397f91f7869f10a255bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 15:21:23 +0900 Subject: [PATCH 25/74] =?UTF-8?q?[BE]=20=EC=9A=B4=EC=98=81=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=EB=B0=B0=ED=8F=AC=20(#694)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#693) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 * [BE] 클라이언트 에러 로그 없애기 (#692) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --- .../main/java/site/coduo/common/config/web/FilterConfig.java | 2 +- .../src/main/java/site/coduo/sync/service/SseEventStream.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java index 465cfd246..486e2801f 100644 --- a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java @@ -39,7 +39,7 @@ public FilterRegistrationBean accessTokenSessionFilter public FilterRegistrationBean signInCookieFilter(final JwtProvider jwtProvider) { final FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setFilter(new SignInCookieFilter(jwtProvider)); - bean.addUrlPatterns("/api/sign-out", "/api/member", "/api/sign-in/check"); + bean.addUrlPatterns("/api/sign-out", "/api/member"); bean.setOrder(1); return bean; } diff --git a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java index 6a4bab98c..b764ffcb6 100644 --- a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java +++ b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java @@ -59,7 +59,7 @@ public void flush(final String name, final String message) { .name(name) .data(message) ); - } catch (IOException e) { + } catch (final IOException e) { log.warn("SSE 통신 중 에러가 발생했습니다."); } } From cb70e63092ec317206df56ee62a4cf648c2be5a9 Mon Sep 17 00:00:00 2001 From: greetings1012 Date: Mon, 7 Oct 2024 15:41:44 +0900 Subject: [PATCH 26/74] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=EB=B8=8C=EB=9E=9C?= =?UTF-8?q?=EC=B9=98=20=EC=9D=B4=EB=A6=84=20=EC=B5=9C=EB=8C=80=20=EA=B8=B8?= =?UTF-8?q?=EC=9D=B4=2030=EC=9E=90=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/PairRoomOnboarding/usePairRoomMission.ts | 2 +- frontend/src/validations/validateBranchName.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/hooks/PairRoomOnboarding/usePairRoomMission.ts b/frontend/src/hooks/PairRoomOnboarding/usePairRoomMission.ts index 387bb8377..8e774f9af 100644 --- a/frontend/src/hooks/PairRoomOnboarding/usePairRoomMission.ts +++ b/frontend/src/hooks/PairRoomOnboarding/usePairRoomMission.ts @@ -10,7 +10,7 @@ const usePairRoomMission = () => { const { value, status, message, handleChange, resetValue } = useInput(); const isRepositorySelected = repositoryName !== ''; - const isValidBranchName = status === 'DEFAULT' && value !== ''; + const isValidBranchName = status === 'DEFAULT' && value !== '' && value.length <= 30; const handleRepositoryName = (name: string) => { setRepositoryName(name); diff --git a/frontend/src/validations/validateBranchName.ts b/frontend/src/validations/validateBranchName.ts index 12ca8b403..4c262bfac 100644 --- a/frontend/src/validations/validateBranchName.ts +++ b/frontend/src/validations/validateBranchName.ts @@ -2,7 +2,7 @@ import type { InputStatus } from '@/components/common/Input/Input.type'; export const validateBranchName = (name: string, branches: string[]) => { if (name.trim() === '') return { status: 'ERROR' as InputStatus, message: '값을 입력해 주세요.' }; - if (name.length > 10) return { status: 'ERROR' as InputStatus, message: '10자 이하로 입력해 주세요.' }; + if (name.length > 30) return { status: 'ERROR' as InputStatus, message: '30자 이하로 입력해 주세요.' }; if (branches.includes(name)) return { status: 'ERROR' as InputStatus, message: '중복된 브랜치 이름 입니다.' }; return { status: 'DEFAULT' as InputStatus, message: '' }; From c456e11b7aa8a99c9a1576e01d7731b5528df521 Mon Sep 17 00:00:00 2001 From: greetings1012 Date: Mon, 7 Oct 2024 15:42:17 +0900 Subject: [PATCH 27/74] =?UTF-8?q?=F0=9F=92=84=20=ED=8E=98=EC=96=B4?= =?UTF-8?q?=EB=A3=B8=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EA=B9=A8=EC=A7=80=EB=8D=98=20=EC=98=A4=EB=A5=98=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PairListSection/PairListSection.styles.ts | 4 ++++ .../PairRoom/PairRoleCard/PairRoleCard.styles.ts | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/frontend/src/components/PairRoom/PairListCard/PairListSection/PairListSection.styles.ts b/frontend/src/components/PairRoom/PairListCard/PairListSection/PairListSection.styles.ts index ea9995a43..c7dd88410 100644 --- a/frontend/src/components/PairRoom/PairListCard/PairListSection/PairListSection.styles.ts +++ b/frontend/src/components/PairRoom/PairListCard/PairListSection/PairListSection.styles.ts @@ -27,5 +27,9 @@ export const PairRole = styled.span<{ $role: Role }>` `; export const PairName = styled.span` + overflow: hidden; + font-size: ${({ theme }) => theme.fontSize.base}; + text-overflow: ellipsis; + white-space: nowrap; `; diff --git a/frontend/src/components/PairRoom/PairRoleCard/PairRoleCard.styles.ts b/frontend/src/components/PairRoom/PairRoleCard/PairRoleCard.styles.ts index 51d7e34d1..7a9728aac 100644 --- a/frontend/src/components/PairRoom/PairRoleCard/PairRoleCard.styles.ts +++ b/frontend/src/components/PairRoom/PairRoleCard/PairRoleCard.styles.ts @@ -76,9 +76,21 @@ export const NavigatorLabel = styled(RoleLabel)` `; export const DriverText = styled.p` + overflow: hidden; + + max-width: 10rem; + color: ${({ theme }) => theme.color.secondary[900]}; + text-overflow: ellipsis; + white-space: nowrap; `; export const NavigatorText = styled.p` + overflow: hidden; + + max-width: 10rem; + color: ${({ theme }) => theme.color.primary[800]}; + text-overflow: ellipsis; + white-space: nowrap; `; From e06417d6831ddf6caad82ffcf0cdb31cacbb0efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 15:45:37 +0900 Subject: [PATCH 28/74] =?UTF-8?q?[BE]=20OAuth=20=EC=84=B8=EC=85=98=20?= =?UTF-8?q?=EB=A9=94=EB=AA=A8=EB=A6=AC=20=EC=82=AC=EC=9A=A9=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=9D=BC=EC=8B=9C=20=EC=82=AD=EC=A0=9C=20(#695)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 --- .../site/coduo/common/config/web/FilterConfig.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java index 486e2801f..17811b6ae 100644 --- a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java @@ -10,22 +10,12 @@ import site.coduo.common.config.web.filter.AccessTokenSessionFilter; import site.coduo.common.config.web.filter.AuthFailHandlerFilter; import site.coduo.common.config.web.filter.SignInCookieFilter; -import site.coduo.common.config.web.filter.StateSessionFilter; import site.coduo.member.infrastructure.security.JwtProvider; @RequiredArgsConstructor @Configuration public class FilterConfig { - @Bean - public FilterRegistrationBean stateSessionFilter() { - final FilterRegistrationBean bean = new FilterRegistrationBean<>(); - bean.setFilter(new StateSessionFilter()); - bean.addUrlPatterns("/api/github/callback"); - bean.setOrder(1); - return bean; - } - @Bean public FilterRegistrationBean accessTokenSessionFilter() { final FilterRegistrationBean bean = new FilterRegistrationBean<>(); From 26c26d3464f6c10789502027605a3e1581db5f8b Mon Sep 17 00:00:00 2001 From: GimGayeon Date: Mon, 7 Oct 2024 16:53:39 +0900 Subject: [PATCH 29/74] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=EB=A0=88=ED=8D=BC?= =?UTF-8?q?=EB=9F=B0=EC=8A=A4=20=EC=82=AC=EC=A7=84=20=EB=88=8C=EB=A0=80?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C=20=EB=A7=81=ED=81=AC=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReferenceList/Reference/Reference.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/PairRoom/ReferenceCard/ReferenceList/Reference/Reference.tsx b/frontend/src/components/PairRoom/ReferenceCard/ReferenceList/Reference/Reference.tsx index 90d9af182..f5c197ab3 100644 --- a/frontend/src/components/PairRoom/ReferenceCard/ReferenceList/Reference/Reference.tsx +++ b/frontend/src/components/PairRoom/ReferenceCard/ReferenceList/Reference/Reference.tsx @@ -14,16 +14,16 @@ const Reference = ({ url, image, title, description, onDeleteReference }: Refere return ( - {image ? ( - - ) : ( - - 이미지가 -
- 없습니다 -
- )} + {image ? ( + + ) : ( + + 이미지가 +
+ 없습니다 +
+ )} {title} {description} From 8496c24f19be3a4f27be10705897ac93768f6b85 Mon Sep 17 00:00:00 2001 From: anttiey Date: Mon, 7 Oct 2024 16:54:12 +0900 Subject: [PATCH 30/74] =?UTF-8?q?:recycle:=20=EC=9D=BC=EC=A0=95=20?= =?UTF-8?q?=EB=B9=88=EB=8F=84=20=EC=9D=B4=EC=83=81=20=ED=83=80=EC=9D=B4?= =?UTF-8?q?=EB=A8=B8=20=EC=97=90=EB=9F=AC=20=EB=B0=9C=EC=83=9D=20=EC=8B=9C?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.tsx | 10 ++++-- frontend/src/hooks/PairRoom/useTimer.ts | 13 +++---- frontend/src/pages/Error/Error.styles.ts | 36 +++++++++++++++++++ frontend/src/pages/Error/Error.tsx | 23 ++++++++++++ .../src/pages/Error/PageNotFound.styles.ts | 27 -------------- frontend/src/pages/Error/PageNotFound.tsx | 26 -------------- 6 files changed, 73 insertions(+), 62 deletions(-) create mode 100644 frontend/src/pages/Error/Error.styles.ts create mode 100644 frontend/src/pages/Error/Error.tsx delete mode 100644 frontend/src/pages/Error/PageNotFound.styles.ts delete mode 100644 frontend/src/pages/Error/PageNotFound.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9a522b81c..8ea7cfdf8 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,7 +8,7 @@ const PairRoom = lazy(() => import('@/pages/PairRoom/PairRoom')); import Callback from '@/pages/Callback/Callback'; import CoduoDocs from '@/pages/CoduoDocs/CoduoDocs'; -import PageNotFound from '@/pages/Error/PageNotFound'; +import Error from '@/pages/Error/Error'; import Landing from '@/pages/Landing/Landing'; import Layout from '@/pages/Layout'; import Loading from '@/pages/Loading/Loading'; @@ -52,7 +52,7 @@ const App = () => { { path: '/', element: , - errorElement: , + errorElement: , children: [ { path: '', @@ -98,9 +98,13 @@ const App = () => { path: 'my-page', element: , }, + { + path: 'error', + element: , + }, { path: '*', - element: , + element: , }, ], }, diff --git a/frontend/src/hooks/PairRoom/useTimer.ts b/frontend/src/hooks/PairRoom/useTimer.ts index c2c9016bb..58a6a55c0 100644 --- a/frontend/src/hooks/PairRoom/useTimer.ts +++ b/frontend/src/hooks/PairRoom/useTimer.ts @@ -1,4 +1,5 @@ import { useRef, useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; import { useQueryClient } from '@tanstack/react-query'; @@ -14,11 +15,15 @@ import { QUERY_KEYS } from '@/constants/queryKeys'; const STATUS_SSE_KEY = 'timer'; const TIME_SSE_KEY = 'remaining-time'; +const TIMEOUT_LIMIT = 100; const useTimer = (accessCode: string, defaultTime: number, defaultTimeleft: number, onTimerStop: () => void) => { + const navigate = useNavigate(); + const queryClient = useQueryClient(); const alarmAudio = useRef(new Audio(AlarmSound)); + const timeoutCount = useRef(0); const [timeLeft, setTimeLeft] = useState(defaultTimeleft); const [isActive, setIsActive] = useState(false); @@ -93,12 +98,8 @@ const useTimer = (accessCode: string, defaultTime: number, defaultTimeleft: numb sse.addEventListener(STATUS_SSE_KEY, handleStatus); sse.onerror = () => { - setIsActive(false); - stopTimer(accessCode); - addToast({ - status: 'ERROR', - message: '타이머 작동 중 예기치 못한 문제가 발생하였습니다. 타이머를 다시 시작해 주세요.', - }); + timeoutCount.current += 1; + if (timeoutCount.current >= TIMEOUT_LIMIT) navigate('/error'); }; window.addEventListener('beforeunload', handleBeforeUnload); diff --git a/frontend/src/pages/Error/Error.styles.ts b/frontend/src/pages/Error/Error.styles.ts new file mode 100644 index 000000000..5ddf33d57 --- /dev/null +++ b/frontend/src/pages/Error/Error.styles.ts @@ -0,0 +1,36 @@ +import styled, { css } from 'styled-components'; + +export const buttonStyles = css` + font-size: ${({ theme }) => theme.fontSize.md}; +`; + +export const Layout = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 6rem; + + height: calc(100vh - 7rem); + padding: 15rem; + + background-color: ${({ theme }) => theme.color.primary[100]}; +`; + +export const TitleContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; +`; + +export const Title = styled.h1` + color: ${({ theme }) => theme.color.primary[800]}; + font-size: ${({ theme }) => theme.fontSize.h1}; + font-weight: bold; +`; + +export const SubTitle = styled.p` + font-size: ${({ theme }) => theme.fontSize.md}; + line-height: 1.5; + text-align: center; +`; diff --git a/frontend/src/pages/Error/Error.tsx b/frontend/src/pages/Error/Error.tsx new file mode 100644 index 000000000..534e9f073 --- /dev/null +++ b/frontend/src/pages/Error/Error.tsx @@ -0,0 +1,23 @@ +import { Link } from 'react-router-dom'; + +import Button from '@/components/common/Button/Button'; + +import * as S from './Error.styles'; + +const Error = () => { + return ( + + + 404 + 페이지를 불러오는 중 문제가 발생했습니다. + + + + + + ); +}; + +export default Error; diff --git a/frontend/src/pages/Error/PageNotFound.styles.ts b/frontend/src/pages/Error/PageNotFound.styles.ts deleted file mode 100644 index 2c4acccef..000000000 --- a/frontend/src/pages/Error/PageNotFound.styles.ts +++ /dev/null @@ -1,27 +0,0 @@ -import styled from 'styled-components'; - -export const Layout = styled.div` - display: flex; - flex-direction: column; - align-items: center; - gap: 2rem; - - height: 100vh; - padding: 10rem; - - background-color: ${({ theme }) => theme.color.primary[100]}; -`; - -export const Title = styled.h1` - font-size: ${({ theme }) => theme.fontSize.h1}; - font-weight: bold; -`; - -export const Description = styled.p` - margin-bottom: 2rem; - - color: ${({ theme }) => theme.color.primary[800]}; - font-size: ${({ theme }) => theme.fontSize.base}; - line-height: 1.5; - text-align: center; -`; diff --git a/frontend/src/pages/Error/PageNotFound.tsx b/frontend/src/pages/Error/PageNotFound.tsx deleted file mode 100644 index 7f9a25884..000000000 --- a/frontend/src/pages/Error/PageNotFound.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Link } from 'react-router-dom'; - -import { LuHome } from 'react-icons/lu'; - -import Button from '@/components/common/Button/Button'; - -import * as S from './PageNotFound.styles'; - -const PageNotFound = () => { - return ( - - 🚨 404 🚨 - - 존재하지 않는 페이지이거나 잘못된 접근입니다.
- 주소를 다시 확인해주세요. 😥 -
- - - -
- ); -}; - -export default PageNotFound; From 034cf887dae3e71ae2a20e41d5da08e009f0cac8 Mon Sep 17 00:00:00 2001 From: anttiey Date: Mon, 7 Oct 2024 17:09:27 +0900 Subject: [PATCH 31/74] =?UTF-8?q?:ambulance:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EC=BD=9C=EB=B0=B1=20=EB=B2=84=EA=B7=B8=20=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Callback/Callback.styles.ts | 13 ++++++------- frontend/src/pages/Callback/Callback.tsx | 13 ++++--------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/frontend/src/pages/Callback/Callback.styles.ts b/frontend/src/pages/Callback/Callback.styles.ts index aa05b112c..d2a6f33cd 100644 --- a/frontend/src/pages/Callback/Callback.styles.ts +++ b/frontend/src/pages/Callback/Callback.styles.ts @@ -4,24 +4,23 @@ export const Layout = styled.div` display: flex; flex-direction: column; align-items: center; - gap: 2rem; + gap: 6rem; height: calc(100vh - 7rem); - padding: 20px; + padding: 15rem 5rem; background-color: ${({ theme }) => theme.color.black[20]}; `; -export const LogoIconWithTitle = styled.img` +export const LogoIcon = styled.img` width: 30rem; max-width: 40rem; - margin: 5rem; `; export const Title = styled.h1` - margin-bottom: 2rem; - color: ${({ theme }) => theme.color.primary[800]}; font-size: ${({ theme }) => theme.fontSize.h5}; - font-weight: ${({ theme }) => theme.fontWeight.bold}; + font-weight: ${({ theme }) => theme.fontWeight.medium}; + line-height: 1.5; + text-align: center; `; diff --git a/frontend/src/pages/Callback/Callback.tsx b/frontend/src/pages/Callback/Callback.tsx index e1b2a4cb6..8a752f0ad 100644 --- a/frontend/src/pages/Callback/Callback.tsx +++ b/frontend/src/pages/Callback/Callback.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { LogoIconWithTitle } from '@/assets'; @@ -15,15 +15,9 @@ import * as S from './Callback.styles'; const Callback = () => { const navigate = useNavigate(); - const hasCalledBack = useRef(false); - const { setUser } = useUserStore(); const handleCallBack = async () => { - if (hasCalledBack.current) return; - - hasCalledBack.current = true; - const { signedUp } = await getSignInCallback(); if (signedUp) { @@ -31,6 +25,7 @@ const Callback = () => { setUser(username, 'SIGNED_IN'); navigate('/main'); + return; } @@ -43,8 +38,8 @@ const Callback = () => { return ( - - 로그인 중입니다! 잠시만 기다려주세요 ☺️ + + 로그인 중입니다. 잠시만 기다려주세요 😊 ); From 0db07a6fcfdfc08df6a0abccb945917e8f3a5335 Mon Sep 17 00:00:00 2001 From: GimGayeon Date: Mon, 7 Oct 2024 17:25:01 +0900 Subject: [PATCH 32/74] =?UTF-8?q?=F0=9F=9A=91=20=20HandleCallback=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20useEffect=20=EB=82=B4=EB=B6=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Callback/Callback.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/frontend/src/pages/Callback/Callback.tsx b/frontend/src/pages/Callback/Callback.tsx index 8a752f0ad..38a8812f8 100644 --- a/frontend/src/pages/Callback/Callback.tsx +++ b/frontend/src/pages/Callback/Callback.tsx @@ -17,24 +17,24 @@ const Callback = () => { const { setUser } = useUserStore(); - const handleCallBack = async () => { - const { signedUp } = await getSignInCallback(); + useEffect(() => { + const handleCallBack = async () => { + const { signedUp } = await getSignInCallback(); - if (signedUp) { - const { username } = await getMember(); + if (signedUp) { + const { username } = await getMember(); - setUser(username, 'SIGNED_IN'); - navigate('/main'); + setUser(username, 'SIGNED_IN'); + navigate('/main'); - return; - } + return; + } - navigate('/sign-up'); - }; + navigate('/sign-up'); + }; - useEffect(() => { handleCallBack(); - }, []); + }, [navigate]); return ( From f9499c2adc10eedda2ed05b7a64cc7567906bee1 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Mon, 7 Oct 2024 17:36:34 +0900 Subject: [PATCH 33/74] =?UTF-8?q?fix:=20SSE=20=ED=83=80=EC=9E=84=EC=95=84?= =?UTF-8?q?=EC=9B=83=EC=9D=B4=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=A9=B4=20?= =?UTF-8?q?=ED=81=B4=EB=9D=BC=EC=9D=B4=EC=96=B8=ED=8A=B8=EA=B0=80=201ms=20?= =?UTF-8?q?=EC=95=88=EC=97=90=20=EC=97=B0=EA=B2=B0=20=EC=9E=AC=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=EC=9D=84=20=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/site/coduo/sync/service/SseEventStream.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java index b764ffcb6..7cf36a495 100644 --- a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java +++ b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java @@ -42,7 +42,8 @@ public SseEmitter connect() { sseEmitter.send(SseEmitter.event() .id(eventId) .name(CONNECT_NAME) - .data(SUCCESS_MESSAGE)); + .data(SUCCESS_MESSAGE) + .reconnectTime(1)); } catch (final IOException e) { throw new SseConnectionFailureException("SSE 연결이 실패했습니다."); } From 5fbfa2c66add69e4c1947e4766195ff065f31e28 Mon Sep 17 00:00:00 2001 From: GimGayeon Date: Mon, 7 Oct 2024 17:56:17 +0900 Subject: [PATCH 34/74] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=AC=B4=ED=95=9C=20=EC=9A=94=EC=B2=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Callback/Callback.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/Callback/Callback.tsx b/frontend/src/pages/Callback/Callback.tsx index 38a8812f8..eb7639903 100644 --- a/frontend/src/pages/Callback/Callback.tsx +++ b/frontend/src/pages/Callback/Callback.tsx @@ -34,7 +34,7 @@ const Callback = () => { }; handleCallBack(); - }, [navigate]); + }, []); return ( From 7c44cc467286531cacc9e3b43ceda54afc923ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 18:01:41 +0900 Subject: [PATCH 35/74] =?UTF-8?q?[BE]=20=EC=84=B8=EC=85=98=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=BF=A0=ED=82=A4=EB=A1=9C=20=EB=8C=80?= =?UTF-8?q?=EC=B2=B4=20(#706)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --- .../coduo/common/config/web/FilterConfig.java | 9 +-- .../web/filter/AccessTokenCookieFilter.java | 56 +++++++++++++++++++ .../web/filter/AccessTokenSessionFilter.java | 1 - .../member/controller/AuthController.java | 20 +++---- .../controller/GithubOAuthController.java | 14 ++--- .../coduo/member/service/AuthService.java | 3 +- .../member/service/GithubOAuthService.java | 7 ++- .../coduo/member/service/MemberService.java | 3 +- .../service/dto/auth/AccessTokenCookie.java | 28 ++++++++++ .../member/service/dto/auth/SignInCookie.java | 12 ++-- .../coduo/acceptance/AuthAcceptanceTest.java | 14 +++-- .../acceptance/GithubAcceptanceTest.java | 31 ++-------- .../acceptance/MemberAcceptanceTest.java | 2 +- .../coduo/member/service/AuthServiceTest.java | 11 ++-- .../service/GithubOAuthServiceTest.java | 11 +++- .../member/service/MemberServiceTest.java | 3 +- 16 files changed, 152 insertions(+), 73 deletions(-) create mode 100644 backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java create mode 100644 backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java diff --git a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java index 17811b6ae..a7af5ec93 100644 --- a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; -import site.coduo.common.config.web.filter.AccessTokenSessionFilter; +import site.coduo.common.config.web.filter.AccessTokenCookieFilter; import site.coduo.common.config.web.filter.AuthFailHandlerFilter; import site.coduo.common.config.web.filter.SignInCookieFilter; import site.coduo.member.infrastructure.security.JwtProvider; @@ -17,9 +17,10 @@ public class FilterConfig { @Bean - public FilterRegistrationBean accessTokenSessionFilter() { - final FilterRegistrationBean bean = new FilterRegistrationBean<>(); - bean.setFilter(new AccessTokenSessionFilter()); + public FilterRegistrationBean accessTokenSessionFilter(final JwtProvider jwtProvider) { + final FilterRegistrationBean bean = new FilterRegistrationBean<>(); + bean.setFilter(new AccessTokenCookieFilter(jwtProvider)); + bean.addUrlPatterns("/api/sign-up", "/api/sign-in/callback"); bean.setOrder(2); return bean; diff --git a/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java new file mode 100644 index 000000000..8a88de634 --- /dev/null +++ b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java @@ -0,0 +1,56 @@ +package site.coduo.common.config.web.filter; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; + +import lombok.RequiredArgsConstructor; +import site.coduo.member.exception.AuthenticationException; +import site.coduo.member.infrastructure.security.JwtProvider; + +@RequiredArgsConstructor +public class AccessTokenCookieFilter implements Filter { + + public static final String TEMPORARY_ACCESS_TOKEN_COOKIE_NAME = "temp_access"; + + private final JwtProvider jwtProvider; + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) + throws IOException, ServletException { + final HttpServletRequest httpRequest = (HttpServletRequest) request; + if (httpRequest.getMethod().equalsIgnoreCase("OPTIONS")) { + chain.doFilter(request, response); + return; + } + validate(parseSignInCookie(httpRequest)); + chain.doFilter(request, response); + } + + private Cookie parseSignInCookie(final HttpServletRequest request) { + final Cookie[] cookies = request.getCookies(); + if (Objects.isNull(cookies)) { + throw new AuthenticationException("쿠키 값이 비어있습니다."); + } + + return Arrays.stream(cookies) + .filter(cookie -> cookie.getName().equals(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME)) + .findAny() + .orElseThrow(() -> new AuthenticationException("임시 엑세스 토큰 쿠키를 찾을 수 없습니다.")); + } + + private void validate(final Cookie cookie) { + if (jwtProvider.isValid(cookie.getValue())) { + return; + } + throw new AuthenticationException("임시 엑세스 토큰 쿠키 값이 유효하지 않습니다."); + } +} diff --git a/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java index 0308eeb9e..cb5c9fce3 100644 --- a/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java +++ b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java @@ -22,7 +22,6 @@ public class AccessTokenSessionFilter implements SessionFilter { public static final String ACCESS_TOKEN_SESSION_NAME = "access token"; - public static final int ACCESS_TOKEN_EXPIRE_IN_SECOND = 600; @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 8510d5f78..4536c5156 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -1,6 +1,6 @@ package site.coduo.member.controller; -import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_SESSION_NAME; +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; import static site.coduo.common.config.web.filter.SignInCookieFilter.SIGN_IN_COOKIE_NAME; import java.net.URI; @@ -15,13 +15,13 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.SessionAttribute; import lombok.RequiredArgsConstructor; import site.coduo.member.controller.docs.AuthControllerDocs; import site.coduo.member.service.AuthService; import site.coduo.member.service.MemberService; import site.coduo.member.service.dto.SignInServiceResponse; +import site.coduo.member.service.dto.auth.AccessTokenCookie; import site.coduo.member.service.dto.auth.SignInCheckResponse; import site.coduo.member.service.dto.auth.SignInCookie; import site.coduo.member.service.dto.auth.SignInWebResponse; @@ -39,18 +39,18 @@ public class AuthController implements AuthControllerDocs { @GetMapping("/sign-out") public ResponseEntity signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) { - final SignInCookie cookie = new SignInCookie(signInToken); + final ResponseCookie expire = SignInCookie.expire(frontUrl); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, cookie.expire(frontUrl).toString()) + .header(HttpHeaders.SET_COOKIE, expire.toString()) .build(); } @PostMapping("/sign-up") public ResponseEntity signUp(@RequestBody final SignUpRequest request, - @SessionAttribute(name = ACCESS_TOKEN_SESSION_NAME) final String accessToken + @CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken ) { - memberService.createMember(request.username(), accessToken); + memberService.createMember(request.username(), encryptedAccessToken); return ResponseEntity.status(HttpStatus.FOUND) .location(URI.create("/api/sign-in/callback")) @@ -59,13 +59,13 @@ public ResponseEntity signUp(@RequestBody final SignUpRequest request, @GetMapping("/sign-in/callback") public ResponseEntity signInCallback( - @SessionAttribute(name = ACCESS_TOKEN_SESSION_NAME) final String accessToken + @CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken ) { - final SignInServiceResponse serviceResponse = authService.createSignInToken(accessToken); - final ResponseCookie cookie = new SignInCookie(serviceResponse.token()).generate(frontUrl); + final SignInServiceResponse serviceResponse = authService.createSignInToken(encryptedAccessToken); + final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(frontUrl); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, cookie.toString()) + .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), AccessTokenCookie.expire(frontUrl).toString()) .body(SignInWebResponse.of(serviceResponse)); } diff --git a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java index d88957b06..7471cb93f 100644 --- a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java @@ -1,8 +1,6 @@ package site.coduo.member.controller; -import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_EXPIRE_IN_SECOND; -import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_SESSION_NAME; import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND; import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME; @@ -11,7 +9,9 @@ import jakarta.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; @@ -19,9 +19,9 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import site.coduo.member.client.dto.TokenResponse; import site.coduo.member.controller.docs.GithubOAuthControllerDocs; import site.coduo.member.service.GithubOAuthService; +import site.coduo.member.service.dto.auth.AccessTokenCookie; import site.coduo.member.service.dto.oauth.GithubAuthQuery; import site.coduo.member.service.dto.oauth.GithubAuthUri; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; @@ -51,12 +51,12 @@ public ResponseEntity getGithubAuthCode(final HttpSession s @GetMapping("/github/callback") public ResponseEntity getAccessToken(@ModelAttribute final GithubCallbackQuery query, final HttpSession session) { - final TokenResponse tokenResponse = githubOAuthService.invokeOAuthCallback(query.code()); - - session.setAttribute(ACCESS_TOKEN_SESSION_NAME, tokenResponse.accessToken()); - session.setMaxInactiveInterval(ACCESS_TOKEN_EXPIRE_IN_SECOND); + final String encryptedAccessToken = githubOAuthService.invokeOAuthCallback(query.code()); + final AccessTokenCookie cookie = new AccessTokenCookie(encryptedAccessToken); + final ResponseCookie responseCookie = cookie.generate(frontUrl); return ResponseEntity.status(HttpStatus.FOUND) + .header(HttpHeaders.SET_COOKIE, responseCookie.toString()) .location(URI.create("https://" + frontUrl + "/callback")) .build(); } diff --git a/backend/src/main/java/site/coduo/member/service/AuthService.java b/backend/src/main/java/site/coduo/member/service/AuthService.java index 41878625b..176acecb2 100644 --- a/backend/src/main/java/site/coduo/member/service/AuthService.java +++ b/backend/src/main/java/site/coduo/member/service/AuthService.java @@ -22,7 +22,8 @@ public class AuthService { private final JwtProvider jwtProvider; @Transactional - public SignInServiceResponse createSignInToken(final String accessToken) { + public SignInServiceResponse createSignInToken(final String encryptedAccessToken) { + final String accessToken = jwtProvider.extractSubject(encryptedAccessToken); final GithubUserResponse userResponse = githubApiClient.getUser(new GithubUserRequest(accessToken)); memberRepository.findByUserId(userResponse.userId()) diff --git a/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java b/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java index f515f7b9f..30ffda460 100644 --- a/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java +++ b/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java @@ -7,6 +7,7 @@ import site.coduo.member.client.GithubOAuthClient; import site.coduo.member.client.dto.TokenRequest; import site.coduo.member.client.dto.TokenResponse; +import site.coduo.member.infrastructure.security.JwtProvider; import site.coduo.member.infrastructure.security.NonceProvider; import site.coduo.member.service.dto.oauth.GithubAuthQuery; @@ -17,6 +18,7 @@ public class GithubOAuthService { private final GithubOAuthClient oAuthClient; private final NonceProvider nonceProvider; + private final JwtProvider jwtProvider; public GithubAuthQuery createAuthorizationContent() { @@ -27,8 +29,9 @@ public GithubAuthQuery createAuthorizationContent() { ); } - public TokenResponse invokeOAuthCallback(final String code) { + public String invokeOAuthCallback(final String code) { String redirectUri = oAuthClient.getOAuthRedirectUri(); - return oAuthClient.grant(new TokenRequest(code, redirectUri)); + final TokenResponse tokenResponse = oAuthClient.grant(new TokenRequest(code, redirectUri)); + return jwtProvider.sign(tokenResponse.accessToken()); } } diff --git a/backend/src/main/java/site/coduo/member/service/MemberService.java b/backend/src/main/java/site/coduo/member/service/MemberService.java index 6fc45d9ed..1212f6482 100644 --- a/backend/src/main/java/site/coduo/member/service/MemberService.java +++ b/backend/src/main/java/site/coduo/member/service/MemberService.java @@ -26,7 +26,8 @@ public class MemberService { private final JwtProvider jwtProvider; @Transactional - public void createMember(final String username, final String accessToken) { + public void createMember(final String username, final String encryptedAccessToken) { + final String accessToken = jwtProvider.extractSubject(encryptedAccessToken); final Bearer bearer = new Bearer(accessToken); final GithubUserResponse userResponse = githubClient.getUser(new GithubUserRequest(bearer)); final Member member = userResponse.toDomain(bearer, username); diff --git a/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java new file mode 100644 index 000000000..25da97087 --- /dev/null +++ b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java @@ -0,0 +1,28 @@ +package site.coduo.member.service.dto.auth; + +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; + +import java.time.Duration; + +import org.springframework.http.ResponseCookie; + +public record AccessTokenCookie(String accessToken) { + + public static ResponseCookie expire(final String domain) { + return ResponseCookie.from(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) + .maxAge(Duration.ZERO) + .domain(domain) + .path("/") + .build(); + } + + public ResponseCookie generate(final String domain) { + return ResponseCookie.from(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) + .value(accessToken) + .httpOnly(true) + .secure(true) + .domain(domain) + .path("/") + .build(); + } +} diff --git a/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java b/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java index 0e9fcc55c..099076c23 100644 --- a/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java +++ b/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java @@ -8,19 +8,19 @@ public record SignInCookie(String credential) { - public ResponseCookie generate(final String domain) { + public static ResponseCookie expire(final String domain) { return ResponseCookie.from(SIGN_IN_COOKIE_NAME) - .value(credential) - .httpOnly(true) - .secure(true) + .maxAge(Duration.ZERO) .domain(domain) .path("/") .build(); } - public ResponseCookie expire(final String domain) { + public ResponseCookie generate(final String domain) { return ResponseCookie.from(SIGN_IN_COOKIE_NAME) - .maxAge(Duration.ZERO) + .value(credential) + .httpOnly(true) + .secure(true) .domain(domain) .path("/") .build(); diff --git a/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java index c68ccf203..3494df21a 100644 --- a/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java @@ -2,6 +2,8 @@ import static org.hamcrest.Matchers.is; +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; + import java.util.Map; import org.apache.http.HttpStatus; @@ -28,7 +30,7 @@ class AuthAcceptanceTest extends AcceptanceFixture { @Test @DisplayName("로그인 검증 & 로그인 토큰을 발급한다.") void verify_login_and_publish_login_token() { - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String cookie = GithubAcceptanceTest.createAccessTokenCookie(); final Member member = createMember(); memberRepository.save(member); @@ -36,7 +38,7 @@ void verify_login_and_publish_login_token() { // when RestAssured .given() - .cookie("JSESSIONID", sessionId) + .cookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME, cookie) .when() .get("/api/sign-in/callback") @@ -50,12 +52,12 @@ void verify_login_and_publish_login_token() { @Test @DisplayName("로그인 검증 & 로그인 토큰을 발급한다. - 로그인 실패 케이스") void verify_login_and_publish_login_token_dose_not_exists_case() { - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String tempCookie = GithubAcceptanceTest.createAccessTokenCookie(); // when RestAssured .given() - .cookie("JSESSIONID", sessionId) + .cookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME, tempCookie) .when() .get("/api/sign-in/callback") @@ -107,7 +109,7 @@ void check_member_login_state() { @DisplayName("인가 정보를 통해 회원가입을 한다.") void sign_up_via_authorization_info() { // given - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String tempCookie = GithubAcceptanceTest.createAccessTokenCookie(); final Map body = Map.of("username", "닉네임"); // when & then @@ -115,7 +117,7 @@ void sign_up_via_authorization_info() { .given().log().all() .contentType(ContentType.JSON) .body(body) - .cookie("JSESSIONID", sessionId) + .cookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME, tempCookie) .when() .post("/api/sign-up") diff --git a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java index a71cc619d..72866994f 100644 --- a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java @@ -3,6 +3,8 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; + import java.util.Map; import org.apache.http.HttpStatus; @@ -18,16 +20,13 @@ class GithubAcceptanceTest extends AcceptanceFixture { - static String createAccessTokenThenReturnSessionId() { - final String session = callAuthorizeThenReturnSessionId(); - + static String createAccessTokenCookie() { final Map query = Map.of("code", "authorization code", "state", FixedNonceProvider.FIXED_VALUE); - RestAssured + return RestAssured .given() .queryParams(query) - .sessionId("JSESSIONID", session) .redirects() .follow(false) .log().all() @@ -35,23 +34,8 @@ static String createAccessTokenThenReturnSessionId() { .when() .get("/api/github/callback") - .then() - .statusCode(HttpStatus.SC_MOVED_TEMPORARILY); - - return session; - } - - static String callAuthorizeThenReturnSessionId() { - return RestAssured - .given() - .redirects() - .follow(false) - - .when() - .get("/api/sign-in/oauth/github") - .thenReturn() - .getSessionId(); + .getCookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME); } @Test @@ -92,8 +76,6 @@ void call_github_authorize_endpoint() { @DisplayName("callback 엔드포인트 호출") void call_callback_end_point() { // given - final String session = callAuthorizeThenReturnSessionId(); - final Map query = Map.of("code", "authorization code", "state", FixedNonceProvider.FIXED_VALUE); @@ -101,7 +83,6 @@ void call_callback_end_point() { RestAssured .given() .queryParams(query) - .sessionId("JSESSIONID", session) .redirects() .follow(false) .log().all() @@ -124,7 +105,6 @@ void try_login_when_call_callback_end_point() { .accessToken(FakeGithubOAuthClient.ACCESS_TOKEN.getCredential()) .profileImage(FakeGithubApiClient.PROFILE_IMAGE) .build(); - final String session = callAuthorizeThenReturnSessionId(); final Map query = Map.of("code", "authorization code", "state", FixedNonceProvider.FIXED_VALUE); @@ -134,7 +114,6 @@ void try_login_when_call_callback_end_point() { RestAssured .given() .queryParams(query) - .sessionId("JSESSIONID", session) .redirects() .follow(false) .log().all() diff --git a/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java index 7b3e6a44c..254e92445 100644 --- a/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java @@ -51,7 +51,7 @@ void search_member_info() { } String login(Member member) { - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String sessionId = GithubAcceptanceTest.createAccessTokenCookie(); memberRepository.save(member); diff --git a/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java b/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java index d049ba040..0081d33e4 100644 --- a/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java @@ -35,13 +35,14 @@ void tearDown() { } @Test - @DisplayName("엑세스 토큰으로 회원을 조회한다.") + @DisplayName("JWT로 감싸진 엑세스 토큰으로 회원을 조회한다.") void search_member_by_access_token() { // given final Member member = createMember("username", FakeGithubApiClient.ACCESS_TOKEN, FakeGithubApiClient.USER_ID); + final String sign = jwtProvider.sign(member.getAccessToken()); // when - final SignInServiceResponse signInToken = authService.createSignInToken(member.getAccessToken()); + final SignInServiceResponse signInToken = authService.createSignInToken(sign); // then assertThat(signInToken.signedIn()).isTrue(); @@ -52,9 +53,10 @@ void search_member_by_access_token() { void throw_exception_when_search_by_does_not_exists_access_token() { // given final String token = "does not exist token"; + final String sign = jwtProvider.sign(token); // when - final SignInServiceResponse signInToken = authService.createSignInToken(token); + final SignInServiceResponse signInToken = authService.createSignInToken(sign); // then assertThat(signInToken.token()).isEmpty(); @@ -77,9 +79,10 @@ private Member createMember(final String username, final String accessToken, fin void renewal_member_access_token_when_create_sign_in_token() { // given final Member member = createMember("username", "origin", FakeGithubApiClient.USER_ID); + final String sign = jwtProvider.sign("change"); // when - authService.createSignInToken("change"); + authService.createSignInToken(sign); // then assertThat(memberRepository.findById(member.getId()).orElseThrow()) diff --git a/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java b/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java index b2e7330d9..48be00a77 100644 --- a/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java @@ -1,6 +1,7 @@ package site.coduo.member.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,7 +12,7 @@ import site.coduo.config.TestConfig; import site.coduo.fake.FakeGithubOAuthClient; import site.coduo.fake.FixedNonceProvider; -import site.coduo.member.client.dto.TokenResponse; +import site.coduo.member.infrastructure.security.JwtProvider; import site.coduo.member.service.dto.oauth.GithubAuthQuery; @SpringBootTest @@ -21,6 +22,9 @@ class GithubOAuthServiceTest { @Autowired private GithubOAuthService githubOAuthService; + @Autowired + private JwtProvider jwtProvider; + @Test @DisplayName("인가 요청을 위한 정보를 생성한다.") void create_info_for_authorization_request_to_third_party() { @@ -45,9 +49,10 @@ void get_access_token() { final String code = "code"; // when - final TokenResponse tokenResponse = githubOAuthService.invokeOAuthCallback(code); + final String tempToken = githubOAuthService.invokeOAuthCallback(code); // then - assertThat(tokenResponse.accessToken()).isEqualTo(FakeGithubOAuthClient.ACCESS_TOKEN.getCredential()); + assertThatCode(() -> jwtProvider.extractSubject(tempToken)) + .doesNotThrowAnyException(); } } diff --git a/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java b/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java index 4a3ca5dbe..4dddba76c 100644 --- a/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java @@ -38,10 +38,11 @@ void tearDown() { void save_member() { // given final String credential = "access-token"; + final String token = jwtProvider.sign(credential); final String username = "username"; // when - memberService.createMember(username, credential); + memberService.createMember(username, token); // then assertThat(memberRepository.findAll()).hasSize(1); From 02349d1b4b583ee93d1d31cad9e823e7ecc51e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 19:04:42 +0900 Subject: [PATCH 36/74] =?UTF-8?q?[BE]=20=EC=84=B8=EC=85=98=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=BF=A0=ED=82=A4=EB=A1=9C=20=EB=8C=80?= =?UTF-8?q?=EC=B2=B4=20(#706)=20(#711)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --- .../coduo/common/config/web/FilterConfig.java | 9 +-- .../web/filter/AccessTokenCookieFilter.java | 56 +++++++++++++++++++ .../web/filter/AccessTokenSessionFilter.java | 1 - .../member/controller/AuthController.java | 20 +++---- .../controller/GithubOAuthController.java | 14 ++--- .../coduo/member/service/AuthService.java | 3 +- .../member/service/GithubOAuthService.java | 7 ++- .../coduo/member/service/MemberService.java | 3 +- .../service/dto/auth/AccessTokenCookie.java | 28 ++++++++++ .../member/service/dto/auth/SignInCookie.java | 12 ++-- .../coduo/acceptance/AuthAcceptanceTest.java | 14 +++-- .../acceptance/GithubAcceptanceTest.java | 31 ++-------- .../acceptance/MemberAcceptanceTest.java | 2 +- .../coduo/member/service/AuthServiceTest.java | 11 ++-- .../service/GithubOAuthServiceTest.java | 11 +++- .../member/service/MemberServiceTest.java | 3 +- 16 files changed, 152 insertions(+), 73 deletions(-) create mode 100644 backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java create mode 100644 backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java diff --git a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java index 17811b6ae..a7af5ec93 100644 --- a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; -import site.coduo.common.config.web.filter.AccessTokenSessionFilter; +import site.coduo.common.config.web.filter.AccessTokenCookieFilter; import site.coduo.common.config.web.filter.AuthFailHandlerFilter; import site.coduo.common.config.web.filter.SignInCookieFilter; import site.coduo.member.infrastructure.security.JwtProvider; @@ -17,9 +17,10 @@ public class FilterConfig { @Bean - public FilterRegistrationBean accessTokenSessionFilter() { - final FilterRegistrationBean bean = new FilterRegistrationBean<>(); - bean.setFilter(new AccessTokenSessionFilter()); + public FilterRegistrationBean accessTokenSessionFilter(final JwtProvider jwtProvider) { + final FilterRegistrationBean bean = new FilterRegistrationBean<>(); + bean.setFilter(new AccessTokenCookieFilter(jwtProvider)); + bean.addUrlPatterns("/api/sign-up", "/api/sign-in/callback"); bean.setOrder(2); return bean; diff --git a/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java new file mode 100644 index 000000000..8a88de634 --- /dev/null +++ b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java @@ -0,0 +1,56 @@ +package site.coduo.common.config.web.filter; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; + +import lombok.RequiredArgsConstructor; +import site.coduo.member.exception.AuthenticationException; +import site.coduo.member.infrastructure.security.JwtProvider; + +@RequiredArgsConstructor +public class AccessTokenCookieFilter implements Filter { + + public static final String TEMPORARY_ACCESS_TOKEN_COOKIE_NAME = "temp_access"; + + private final JwtProvider jwtProvider; + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) + throws IOException, ServletException { + final HttpServletRequest httpRequest = (HttpServletRequest) request; + if (httpRequest.getMethod().equalsIgnoreCase("OPTIONS")) { + chain.doFilter(request, response); + return; + } + validate(parseSignInCookie(httpRequest)); + chain.doFilter(request, response); + } + + private Cookie parseSignInCookie(final HttpServletRequest request) { + final Cookie[] cookies = request.getCookies(); + if (Objects.isNull(cookies)) { + throw new AuthenticationException("쿠키 값이 비어있습니다."); + } + + return Arrays.stream(cookies) + .filter(cookie -> cookie.getName().equals(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME)) + .findAny() + .orElseThrow(() -> new AuthenticationException("임시 엑세스 토큰 쿠키를 찾을 수 없습니다.")); + } + + private void validate(final Cookie cookie) { + if (jwtProvider.isValid(cookie.getValue())) { + return; + } + throw new AuthenticationException("임시 엑세스 토큰 쿠키 값이 유효하지 않습니다."); + } +} diff --git a/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java index 0308eeb9e..cb5c9fce3 100644 --- a/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java +++ b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java @@ -22,7 +22,6 @@ public class AccessTokenSessionFilter implements SessionFilter { public static final String ACCESS_TOKEN_SESSION_NAME = "access token"; - public static final int ACCESS_TOKEN_EXPIRE_IN_SECOND = 600; @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 8510d5f78..4536c5156 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -1,6 +1,6 @@ package site.coduo.member.controller; -import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_SESSION_NAME; +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; import static site.coduo.common.config.web.filter.SignInCookieFilter.SIGN_IN_COOKIE_NAME; import java.net.URI; @@ -15,13 +15,13 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.SessionAttribute; import lombok.RequiredArgsConstructor; import site.coduo.member.controller.docs.AuthControllerDocs; import site.coduo.member.service.AuthService; import site.coduo.member.service.MemberService; import site.coduo.member.service.dto.SignInServiceResponse; +import site.coduo.member.service.dto.auth.AccessTokenCookie; import site.coduo.member.service.dto.auth.SignInCheckResponse; import site.coduo.member.service.dto.auth.SignInCookie; import site.coduo.member.service.dto.auth.SignInWebResponse; @@ -39,18 +39,18 @@ public class AuthController implements AuthControllerDocs { @GetMapping("/sign-out") public ResponseEntity signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) { - final SignInCookie cookie = new SignInCookie(signInToken); + final ResponseCookie expire = SignInCookie.expire(frontUrl); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, cookie.expire(frontUrl).toString()) + .header(HttpHeaders.SET_COOKIE, expire.toString()) .build(); } @PostMapping("/sign-up") public ResponseEntity signUp(@RequestBody final SignUpRequest request, - @SessionAttribute(name = ACCESS_TOKEN_SESSION_NAME) final String accessToken + @CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken ) { - memberService.createMember(request.username(), accessToken); + memberService.createMember(request.username(), encryptedAccessToken); return ResponseEntity.status(HttpStatus.FOUND) .location(URI.create("/api/sign-in/callback")) @@ -59,13 +59,13 @@ public ResponseEntity signUp(@RequestBody final SignUpRequest request, @GetMapping("/sign-in/callback") public ResponseEntity signInCallback( - @SessionAttribute(name = ACCESS_TOKEN_SESSION_NAME) final String accessToken + @CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken ) { - final SignInServiceResponse serviceResponse = authService.createSignInToken(accessToken); - final ResponseCookie cookie = new SignInCookie(serviceResponse.token()).generate(frontUrl); + final SignInServiceResponse serviceResponse = authService.createSignInToken(encryptedAccessToken); + final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(frontUrl); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, cookie.toString()) + .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), AccessTokenCookie.expire(frontUrl).toString()) .body(SignInWebResponse.of(serviceResponse)); } diff --git a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java index d88957b06..7471cb93f 100644 --- a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java @@ -1,8 +1,6 @@ package site.coduo.member.controller; -import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_EXPIRE_IN_SECOND; -import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_SESSION_NAME; import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND; import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME; @@ -11,7 +9,9 @@ import jakarta.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; @@ -19,9 +19,9 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import site.coduo.member.client.dto.TokenResponse; import site.coduo.member.controller.docs.GithubOAuthControllerDocs; import site.coduo.member.service.GithubOAuthService; +import site.coduo.member.service.dto.auth.AccessTokenCookie; import site.coduo.member.service.dto.oauth.GithubAuthQuery; import site.coduo.member.service.dto.oauth.GithubAuthUri; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; @@ -51,12 +51,12 @@ public ResponseEntity getGithubAuthCode(final HttpSession s @GetMapping("/github/callback") public ResponseEntity getAccessToken(@ModelAttribute final GithubCallbackQuery query, final HttpSession session) { - final TokenResponse tokenResponse = githubOAuthService.invokeOAuthCallback(query.code()); - - session.setAttribute(ACCESS_TOKEN_SESSION_NAME, tokenResponse.accessToken()); - session.setMaxInactiveInterval(ACCESS_TOKEN_EXPIRE_IN_SECOND); + final String encryptedAccessToken = githubOAuthService.invokeOAuthCallback(query.code()); + final AccessTokenCookie cookie = new AccessTokenCookie(encryptedAccessToken); + final ResponseCookie responseCookie = cookie.generate(frontUrl); return ResponseEntity.status(HttpStatus.FOUND) + .header(HttpHeaders.SET_COOKIE, responseCookie.toString()) .location(URI.create("https://" + frontUrl + "/callback")) .build(); } diff --git a/backend/src/main/java/site/coduo/member/service/AuthService.java b/backend/src/main/java/site/coduo/member/service/AuthService.java index 41878625b..176acecb2 100644 --- a/backend/src/main/java/site/coduo/member/service/AuthService.java +++ b/backend/src/main/java/site/coduo/member/service/AuthService.java @@ -22,7 +22,8 @@ public class AuthService { private final JwtProvider jwtProvider; @Transactional - public SignInServiceResponse createSignInToken(final String accessToken) { + public SignInServiceResponse createSignInToken(final String encryptedAccessToken) { + final String accessToken = jwtProvider.extractSubject(encryptedAccessToken); final GithubUserResponse userResponse = githubApiClient.getUser(new GithubUserRequest(accessToken)); memberRepository.findByUserId(userResponse.userId()) diff --git a/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java b/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java index f515f7b9f..30ffda460 100644 --- a/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java +++ b/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java @@ -7,6 +7,7 @@ import site.coduo.member.client.GithubOAuthClient; import site.coduo.member.client.dto.TokenRequest; import site.coduo.member.client.dto.TokenResponse; +import site.coduo.member.infrastructure.security.JwtProvider; import site.coduo.member.infrastructure.security.NonceProvider; import site.coduo.member.service.dto.oauth.GithubAuthQuery; @@ -17,6 +18,7 @@ public class GithubOAuthService { private final GithubOAuthClient oAuthClient; private final NonceProvider nonceProvider; + private final JwtProvider jwtProvider; public GithubAuthQuery createAuthorizationContent() { @@ -27,8 +29,9 @@ public GithubAuthQuery createAuthorizationContent() { ); } - public TokenResponse invokeOAuthCallback(final String code) { + public String invokeOAuthCallback(final String code) { String redirectUri = oAuthClient.getOAuthRedirectUri(); - return oAuthClient.grant(new TokenRequest(code, redirectUri)); + final TokenResponse tokenResponse = oAuthClient.grant(new TokenRequest(code, redirectUri)); + return jwtProvider.sign(tokenResponse.accessToken()); } } diff --git a/backend/src/main/java/site/coduo/member/service/MemberService.java b/backend/src/main/java/site/coduo/member/service/MemberService.java index 6fc45d9ed..1212f6482 100644 --- a/backend/src/main/java/site/coduo/member/service/MemberService.java +++ b/backend/src/main/java/site/coduo/member/service/MemberService.java @@ -26,7 +26,8 @@ public class MemberService { private final JwtProvider jwtProvider; @Transactional - public void createMember(final String username, final String accessToken) { + public void createMember(final String username, final String encryptedAccessToken) { + final String accessToken = jwtProvider.extractSubject(encryptedAccessToken); final Bearer bearer = new Bearer(accessToken); final GithubUserResponse userResponse = githubClient.getUser(new GithubUserRequest(bearer)); final Member member = userResponse.toDomain(bearer, username); diff --git a/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java new file mode 100644 index 000000000..25da97087 --- /dev/null +++ b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java @@ -0,0 +1,28 @@ +package site.coduo.member.service.dto.auth; + +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; + +import java.time.Duration; + +import org.springframework.http.ResponseCookie; + +public record AccessTokenCookie(String accessToken) { + + public static ResponseCookie expire(final String domain) { + return ResponseCookie.from(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) + .maxAge(Duration.ZERO) + .domain(domain) + .path("/") + .build(); + } + + public ResponseCookie generate(final String domain) { + return ResponseCookie.from(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) + .value(accessToken) + .httpOnly(true) + .secure(true) + .domain(domain) + .path("/") + .build(); + } +} diff --git a/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java b/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java index 0e9fcc55c..099076c23 100644 --- a/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java +++ b/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java @@ -8,19 +8,19 @@ public record SignInCookie(String credential) { - public ResponseCookie generate(final String domain) { + public static ResponseCookie expire(final String domain) { return ResponseCookie.from(SIGN_IN_COOKIE_NAME) - .value(credential) - .httpOnly(true) - .secure(true) + .maxAge(Duration.ZERO) .domain(domain) .path("/") .build(); } - public ResponseCookie expire(final String domain) { + public ResponseCookie generate(final String domain) { return ResponseCookie.from(SIGN_IN_COOKIE_NAME) - .maxAge(Duration.ZERO) + .value(credential) + .httpOnly(true) + .secure(true) .domain(domain) .path("/") .build(); diff --git a/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java index c68ccf203..3494df21a 100644 --- a/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java @@ -2,6 +2,8 @@ import static org.hamcrest.Matchers.is; +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; + import java.util.Map; import org.apache.http.HttpStatus; @@ -28,7 +30,7 @@ class AuthAcceptanceTest extends AcceptanceFixture { @Test @DisplayName("로그인 검증 & 로그인 토큰을 발급한다.") void verify_login_and_publish_login_token() { - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String cookie = GithubAcceptanceTest.createAccessTokenCookie(); final Member member = createMember(); memberRepository.save(member); @@ -36,7 +38,7 @@ void verify_login_and_publish_login_token() { // when RestAssured .given() - .cookie("JSESSIONID", sessionId) + .cookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME, cookie) .when() .get("/api/sign-in/callback") @@ -50,12 +52,12 @@ void verify_login_and_publish_login_token() { @Test @DisplayName("로그인 검증 & 로그인 토큰을 발급한다. - 로그인 실패 케이스") void verify_login_and_publish_login_token_dose_not_exists_case() { - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String tempCookie = GithubAcceptanceTest.createAccessTokenCookie(); // when RestAssured .given() - .cookie("JSESSIONID", sessionId) + .cookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME, tempCookie) .when() .get("/api/sign-in/callback") @@ -107,7 +109,7 @@ void check_member_login_state() { @DisplayName("인가 정보를 통해 회원가입을 한다.") void sign_up_via_authorization_info() { // given - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String tempCookie = GithubAcceptanceTest.createAccessTokenCookie(); final Map body = Map.of("username", "닉네임"); // when & then @@ -115,7 +117,7 @@ void sign_up_via_authorization_info() { .given().log().all() .contentType(ContentType.JSON) .body(body) - .cookie("JSESSIONID", sessionId) + .cookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME, tempCookie) .when() .post("/api/sign-up") diff --git a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java index a71cc619d..72866994f 100644 --- a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java @@ -3,6 +3,8 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; + import java.util.Map; import org.apache.http.HttpStatus; @@ -18,16 +20,13 @@ class GithubAcceptanceTest extends AcceptanceFixture { - static String createAccessTokenThenReturnSessionId() { - final String session = callAuthorizeThenReturnSessionId(); - + static String createAccessTokenCookie() { final Map query = Map.of("code", "authorization code", "state", FixedNonceProvider.FIXED_VALUE); - RestAssured + return RestAssured .given() .queryParams(query) - .sessionId("JSESSIONID", session) .redirects() .follow(false) .log().all() @@ -35,23 +34,8 @@ static String createAccessTokenThenReturnSessionId() { .when() .get("/api/github/callback") - .then() - .statusCode(HttpStatus.SC_MOVED_TEMPORARILY); - - return session; - } - - static String callAuthorizeThenReturnSessionId() { - return RestAssured - .given() - .redirects() - .follow(false) - - .when() - .get("/api/sign-in/oauth/github") - .thenReturn() - .getSessionId(); + .getCookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME); } @Test @@ -92,8 +76,6 @@ void call_github_authorize_endpoint() { @DisplayName("callback 엔드포인트 호출") void call_callback_end_point() { // given - final String session = callAuthorizeThenReturnSessionId(); - final Map query = Map.of("code", "authorization code", "state", FixedNonceProvider.FIXED_VALUE); @@ -101,7 +83,6 @@ void call_callback_end_point() { RestAssured .given() .queryParams(query) - .sessionId("JSESSIONID", session) .redirects() .follow(false) .log().all() @@ -124,7 +105,6 @@ void try_login_when_call_callback_end_point() { .accessToken(FakeGithubOAuthClient.ACCESS_TOKEN.getCredential()) .profileImage(FakeGithubApiClient.PROFILE_IMAGE) .build(); - final String session = callAuthorizeThenReturnSessionId(); final Map query = Map.of("code", "authorization code", "state", FixedNonceProvider.FIXED_VALUE); @@ -134,7 +114,6 @@ void try_login_when_call_callback_end_point() { RestAssured .given() .queryParams(query) - .sessionId("JSESSIONID", session) .redirects() .follow(false) .log().all() diff --git a/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java index 7b3e6a44c..254e92445 100644 --- a/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java @@ -51,7 +51,7 @@ void search_member_info() { } String login(Member member) { - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String sessionId = GithubAcceptanceTest.createAccessTokenCookie(); memberRepository.save(member); diff --git a/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java b/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java index d049ba040..0081d33e4 100644 --- a/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java @@ -35,13 +35,14 @@ void tearDown() { } @Test - @DisplayName("엑세스 토큰으로 회원을 조회한다.") + @DisplayName("JWT로 감싸진 엑세스 토큰으로 회원을 조회한다.") void search_member_by_access_token() { // given final Member member = createMember("username", FakeGithubApiClient.ACCESS_TOKEN, FakeGithubApiClient.USER_ID); + final String sign = jwtProvider.sign(member.getAccessToken()); // when - final SignInServiceResponse signInToken = authService.createSignInToken(member.getAccessToken()); + final SignInServiceResponse signInToken = authService.createSignInToken(sign); // then assertThat(signInToken.signedIn()).isTrue(); @@ -52,9 +53,10 @@ void search_member_by_access_token() { void throw_exception_when_search_by_does_not_exists_access_token() { // given final String token = "does not exist token"; + final String sign = jwtProvider.sign(token); // when - final SignInServiceResponse signInToken = authService.createSignInToken(token); + final SignInServiceResponse signInToken = authService.createSignInToken(sign); // then assertThat(signInToken.token()).isEmpty(); @@ -77,9 +79,10 @@ private Member createMember(final String username, final String accessToken, fin void renewal_member_access_token_when_create_sign_in_token() { // given final Member member = createMember("username", "origin", FakeGithubApiClient.USER_ID); + final String sign = jwtProvider.sign("change"); // when - authService.createSignInToken("change"); + authService.createSignInToken(sign); // then assertThat(memberRepository.findById(member.getId()).orElseThrow()) diff --git a/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java b/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java index b2e7330d9..48be00a77 100644 --- a/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java @@ -1,6 +1,7 @@ package site.coduo.member.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,7 +12,7 @@ import site.coduo.config.TestConfig; import site.coduo.fake.FakeGithubOAuthClient; import site.coduo.fake.FixedNonceProvider; -import site.coduo.member.client.dto.TokenResponse; +import site.coduo.member.infrastructure.security.JwtProvider; import site.coduo.member.service.dto.oauth.GithubAuthQuery; @SpringBootTest @@ -21,6 +22,9 @@ class GithubOAuthServiceTest { @Autowired private GithubOAuthService githubOAuthService; + @Autowired + private JwtProvider jwtProvider; + @Test @DisplayName("인가 요청을 위한 정보를 생성한다.") void create_info_for_authorization_request_to_third_party() { @@ -45,9 +49,10 @@ void get_access_token() { final String code = "code"; // when - final TokenResponse tokenResponse = githubOAuthService.invokeOAuthCallback(code); + final String tempToken = githubOAuthService.invokeOAuthCallback(code); // then - assertThat(tokenResponse.accessToken()).isEqualTo(FakeGithubOAuthClient.ACCESS_TOKEN.getCredential()); + assertThatCode(() -> jwtProvider.extractSubject(tempToken)) + .doesNotThrowAnyException(); } } diff --git a/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java b/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java index 4a3ca5dbe..4dddba76c 100644 --- a/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java @@ -38,10 +38,11 @@ void tearDown() { void save_member() { // given final String credential = "access-token"; + final String token = jwtProvider.sign(credential); final String username = "username"; // when - memberService.createMember(username, credential); + memberService.createMember(username, token); // then assertThat(memberRepository.findAll()).hasSize(1); From e7f60ab144334861ae84b24aa3b89745b485d17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 20:22:12 +0900 Subject: [PATCH 37/74] =?UTF-8?q?[BE]=20=EC=BF=A0=ED=82=A4=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=84=A4=EC=A0=95=20=ED=86=B5=EC=9D=BC?= =?UTF-8?q?=ED=99=94=20=20(#712)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 --- .../java/site/coduo/common/config/web/FilterConfig.java | 1 - .../site/coduo/member/controller/AuthController.java | 9 ++++++--- .../coduo/member/controller/GithubOAuthController.java | 5 +++-- .../java/site/coduo/acceptance/GithubAcceptanceTest.java | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java index a7af5ec93..0e1d3fed7 100644 --- a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java @@ -20,7 +20,6 @@ public class FilterConfig { public FilterRegistrationBean accessTokenSessionFilter(final JwtProvider jwtProvider) { final FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setFilter(new AccessTokenCookieFilter(jwtProvider)); - bean.addUrlPatterns("/api/sign-up", "/api/sign-in/callback"); bean.setOrder(2); return bean; diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 4536c5156..9f99cc641 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -31,6 +31,8 @@ @RestController public class AuthController implements AuthControllerDocs { + public static final String PRODUCT_DOMAIN = "coduo.site"; + private final AuthService authService; private final MemberService memberService; @@ -39,7 +41,7 @@ public class AuthController implements AuthControllerDocs { @GetMapping("/sign-out") public ResponseEntity signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) { - final ResponseCookie expire = SignInCookie.expire(frontUrl); + final ResponseCookie expire = SignInCookie.expire(PRODUCT_DOMAIN); return ResponseEntity.ok() .header(HttpHeaders.SET_COOKIE, expire.toString()) @@ -62,10 +64,11 @@ public ResponseEntity signInCallback( @CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken ) { final SignInServiceResponse serviceResponse = authService.createSignInToken(encryptedAccessToken); - final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(frontUrl); + final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(PRODUCT_DOMAIN); + final ResponseCookie expireTemporaryAccessToken = AccessTokenCookie.expire(PRODUCT_DOMAIN); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), AccessTokenCookie.expire(frontUrl).toString()) + .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), expireTemporaryAccessToken.toString()) .body(SignInWebResponse.of(serviceResponse)); } diff --git a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java index 7471cb93f..8319d7ce4 100644 --- a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java @@ -3,6 +3,7 @@ import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND; import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME; +import static site.coduo.member.controller.AuthController.PRODUCT_DOMAIN; import java.net.URI; @@ -53,9 +54,9 @@ public ResponseEntity getAccessToken(@ModelAttribute final GithubCallbackQ final HttpSession session) { final String encryptedAccessToken = githubOAuthService.invokeOAuthCallback(query.code()); final AccessTokenCookie cookie = new AccessTokenCookie(encryptedAccessToken); - final ResponseCookie responseCookie = cookie.generate(frontUrl); + final ResponseCookie responseCookie = cookie.generate(PRODUCT_DOMAIN); - return ResponseEntity.status(HttpStatus.FOUND) + return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT) .header(HttpHeaders.SET_COOKIE, responseCookie.toString()) .location(URI.create("https://" + frontUrl + "/callback")) .build(); diff --git a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java index 72866994f..350afd1eb 100644 --- a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java @@ -91,7 +91,7 @@ void call_callback_end_point() { .get("/api/github/callback") .then() - .statusCode(HttpStatus.SC_MOVED_TEMPORARILY); + .statusCode(HttpStatus.SC_TEMPORARY_REDIRECT); } @Test @@ -122,6 +122,6 @@ void try_login_when_call_callback_end_point() { .get("/api/github/callback") .then().log().all() - .statusCode(302); + .statusCode(HttpStatus.SC_TEMPORARY_REDIRECT); } } From 90c0dbccc71c6f96e2f602c56a2bc6a161f69177 Mon Sep 17 00:00:00 2001 From: anttiey Date: Mon, 7 Oct 2024 20:23:55 +0900 Subject: [PATCH 38/74] =?UTF-8?q?:ambulance:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EC=BD=9C=EB=B0=B1=20=EB=84=A4=EB=B9=84=EA=B2=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EB=B2=84=EA=B7=B8=20=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Callback/Callback.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/Callback/Callback.tsx b/frontend/src/pages/Callback/Callback.tsx index eb7639903..3db4f9107 100644 --- a/frontend/src/pages/Callback/Callback.tsx +++ b/frontend/src/pages/Callback/Callback.tsx @@ -5,9 +5,9 @@ import { LogoIconWithTitle } from '@/assets'; import Spinner from '@/components/common/Spinner/Spinner'; -import useUserStore from '@/stores/userStore'; +// import useUserStore from '@/stores/userStore'; -import { getMember } from '@/apis/member'; +// import { getMember } from '@/apis/member'; import { getSignInCallback } from '@/apis/oauth'; import * as S from './Callback.styles'; @@ -15,16 +15,16 @@ import * as S from './Callback.styles'; const Callback = () => { const navigate = useNavigate(); - const { setUser } = useUserStore(); + // const { setUser } = useUserStore(); useEffect(() => { const handleCallBack = async () => { const { signedUp } = await getSignInCallback(); if (signedUp) { - const { username } = await getMember(); + // const { username } = await getMember(); - setUser(username, 'SIGNED_IN'); + // setUser(username, 'SIGNED_IN'); navigate('/main'); return; From 28fa31554e31f2ed2e04b3e1f577a14877d85985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 20:25:16 +0900 Subject: [PATCH 39/74] =?UTF-8?q?[BE]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EB=B0=B0=ED=8F=AC=20(#713)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --- .../coduo/common/config/web/FilterConfig.java | 8 +-- .../web/filter/AccessTokenCookieFilter.java | 56 +++++++++++++++++++ .../web/filter/AccessTokenSessionFilter.java | 1 - .../member/controller/AuthController.java | 23 ++++---- .../controller/GithubOAuthController.java | 17 +++--- .../coduo/member/service/AuthService.java | 3 +- .../member/service/GithubOAuthService.java | 7 ++- .../coduo/member/service/MemberService.java | 3 +- .../service/dto/auth/AccessTokenCookie.java | 28 ++++++++++ .../member/service/dto/auth/SignInCookie.java | 12 ++-- .../coduo/acceptance/AuthAcceptanceTest.java | 14 +++-- .../acceptance/GithubAcceptanceTest.java | 35 +++--------- .../acceptance/MemberAcceptanceTest.java | 2 +- .../coduo/member/service/AuthServiceTest.java | 11 ++-- .../service/GithubOAuthServiceTest.java | 11 +++- .../member/service/MemberServiceTest.java | 3 +- 16 files changed, 158 insertions(+), 76 deletions(-) create mode 100644 backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java create mode 100644 backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java diff --git a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java index 17811b6ae..0e1d3fed7 100644 --- a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; -import site.coduo.common.config.web.filter.AccessTokenSessionFilter; +import site.coduo.common.config.web.filter.AccessTokenCookieFilter; import site.coduo.common.config.web.filter.AuthFailHandlerFilter; import site.coduo.common.config.web.filter.SignInCookieFilter; import site.coduo.member.infrastructure.security.JwtProvider; @@ -17,9 +17,9 @@ public class FilterConfig { @Bean - public FilterRegistrationBean accessTokenSessionFilter() { - final FilterRegistrationBean bean = new FilterRegistrationBean<>(); - bean.setFilter(new AccessTokenSessionFilter()); + public FilterRegistrationBean accessTokenSessionFilter(final JwtProvider jwtProvider) { + final FilterRegistrationBean bean = new FilterRegistrationBean<>(); + bean.setFilter(new AccessTokenCookieFilter(jwtProvider)); bean.addUrlPatterns("/api/sign-up", "/api/sign-in/callback"); bean.setOrder(2); return bean; diff --git a/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java new file mode 100644 index 000000000..8a88de634 --- /dev/null +++ b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenCookieFilter.java @@ -0,0 +1,56 @@ +package site.coduo.common.config.web.filter; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; + +import lombok.RequiredArgsConstructor; +import site.coduo.member.exception.AuthenticationException; +import site.coduo.member.infrastructure.security.JwtProvider; + +@RequiredArgsConstructor +public class AccessTokenCookieFilter implements Filter { + + public static final String TEMPORARY_ACCESS_TOKEN_COOKIE_NAME = "temp_access"; + + private final JwtProvider jwtProvider; + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) + throws IOException, ServletException { + final HttpServletRequest httpRequest = (HttpServletRequest) request; + if (httpRequest.getMethod().equalsIgnoreCase("OPTIONS")) { + chain.doFilter(request, response); + return; + } + validate(parseSignInCookie(httpRequest)); + chain.doFilter(request, response); + } + + private Cookie parseSignInCookie(final HttpServletRequest request) { + final Cookie[] cookies = request.getCookies(); + if (Objects.isNull(cookies)) { + throw new AuthenticationException("쿠키 값이 비어있습니다."); + } + + return Arrays.stream(cookies) + .filter(cookie -> cookie.getName().equals(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME)) + .findAny() + .orElseThrow(() -> new AuthenticationException("임시 엑세스 토큰 쿠키를 찾을 수 없습니다.")); + } + + private void validate(final Cookie cookie) { + if (jwtProvider.isValid(cookie.getValue())) { + return; + } + throw new AuthenticationException("임시 엑세스 토큰 쿠키 값이 유효하지 않습니다."); + } +} diff --git a/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java index 0308eeb9e..cb5c9fce3 100644 --- a/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java +++ b/backend/src/main/java/site/coduo/common/config/web/filter/AccessTokenSessionFilter.java @@ -22,7 +22,6 @@ public class AccessTokenSessionFilter implements SessionFilter { public static final String ACCESS_TOKEN_SESSION_NAME = "access token"; - public static final int ACCESS_TOKEN_EXPIRE_IN_SECOND = 600; @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 8510d5f78..9f99cc641 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -1,6 +1,6 @@ package site.coduo.member.controller; -import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_SESSION_NAME; +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; import static site.coduo.common.config.web.filter.SignInCookieFilter.SIGN_IN_COOKIE_NAME; import java.net.URI; @@ -15,13 +15,13 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.SessionAttribute; import lombok.RequiredArgsConstructor; import site.coduo.member.controller.docs.AuthControllerDocs; import site.coduo.member.service.AuthService; import site.coduo.member.service.MemberService; import site.coduo.member.service.dto.SignInServiceResponse; +import site.coduo.member.service.dto.auth.AccessTokenCookie; import site.coduo.member.service.dto.auth.SignInCheckResponse; import site.coduo.member.service.dto.auth.SignInCookie; import site.coduo.member.service.dto.auth.SignInWebResponse; @@ -31,6 +31,8 @@ @RestController public class AuthController implements AuthControllerDocs { + public static final String PRODUCT_DOMAIN = "coduo.site"; + private final AuthService authService; private final MemberService memberService; @@ -39,18 +41,18 @@ public class AuthController implements AuthControllerDocs { @GetMapping("/sign-out") public ResponseEntity signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) { - final SignInCookie cookie = new SignInCookie(signInToken); + final ResponseCookie expire = SignInCookie.expire(PRODUCT_DOMAIN); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, cookie.expire(frontUrl).toString()) + .header(HttpHeaders.SET_COOKIE, expire.toString()) .build(); } @PostMapping("/sign-up") public ResponseEntity signUp(@RequestBody final SignUpRequest request, - @SessionAttribute(name = ACCESS_TOKEN_SESSION_NAME) final String accessToken + @CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken ) { - memberService.createMember(request.username(), accessToken); + memberService.createMember(request.username(), encryptedAccessToken); return ResponseEntity.status(HttpStatus.FOUND) .location(URI.create("/api/sign-in/callback")) @@ -59,13 +61,14 @@ public ResponseEntity signUp(@RequestBody final SignUpRequest request, @GetMapping("/sign-in/callback") public ResponseEntity signInCallback( - @SessionAttribute(name = ACCESS_TOKEN_SESSION_NAME) final String accessToken + @CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken ) { - final SignInServiceResponse serviceResponse = authService.createSignInToken(accessToken); - final ResponseCookie cookie = new SignInCookie(serviceResponse.token()).generate(frontUrl); + final SignInServiceResponse serviceResponse = authService.createSignInToken(encryptedAccessToken); + final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(PRODUCT_DOMAIN); + final ResponseCookie expireTemporaryAccessToken = AccessTokenCookie.expire(PRODUCT_DOMAIN); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, cookie.toString()) + .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), expireTemporaryAccessToken.toString()) .body(SignInWebResponse.of(serviceResponse)); } diff --git a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java index d88957b06..8319d7ce4 100644 --- a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java @@ -1,17 +1,18 @@ package site.coduo.member.controller; -import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_EXPIRE_IN_SECOND; -import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_SESSION_NAME; import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND; import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME; +import static site.coduo.member.controller.AuthController.PRODUCT_DOMAIN; import java.net.URI; import jakarta.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; @@ -19,9 +20,9 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import site.coduo.member.client.dto.TokenResponse; import site.coduo.member.controller.docs.GithubOAuthControllerDocs; import site.coduo.member.service.GithubOAuthService; +import site.coduo.member.service.dto.auth.AccessTokenCookie; import site.coduo.member.service.dto.oauth.GithubAuthQuery; import site.coduo.member.service.dto.oauth.GithubAuthUri; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; @@ -51,12 +52,12 @@ public ResponseEntity getGithubAuthCode(final HttpSession s @GetMapping("/github/callback") public ResponseEntity getAccessToken(@ModelAttribute final GithubCallbackQuery query, final HttpSession session) { - final TokenResponse tokenResponse = githubOAuthService.invokeOAuthCallback(query.code()); + final String encryptedAccessToken = githubOAuthService.invokeOAuthCallback(query.code()); + final AccessTokenCookie cookie = new AccessTokenCookie(encryptedAccessToken); + final ResponseCookie responseCookie = cookie.generate(PRODUCT_DOMAIN); - session.setAttribute(ACCESS_TOKEN_SESSION_NAME, tokenResponse.accessToken()); - session.setMaxInactiveInterval(ACCESS_TOKEN_EXPIRE_IN_SECOND); - - return ResponseEntity.status(HttpStatus.FOUND) + return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT) + .header(HttpHeaders.SET_COOKIE, responseCookie.toString()) .location(URI.create("https://" + frontUrl + "/callback")) .build(); } diff --git a/backend/src/main/java/site/coduo/member/service/AuthService.java b/backend/src/main/java/site/coduo/member/service/AuthService.java index 41878625b..176acecb2 100644 --- a/backend/src/main/java/site/coduo/member/service/AuthService.java +++ b/backend/src/main/java/site/coduo/member/service/AuthService.java @@ -22,7 +22,8 @@ public class AuthService { private final JwtProvider jwtProvider; @Transactional - public SignInServiceResponse createSignInToken(final String accessToken) { + public SignInServiceResponse createSignInToken(final String encryptedAccessToken) { + final String accessToken = jwtProvider.extractSubject(encryptedAccessToken); final GithubUserResponse userResponse = githubApiClient.getUser(new GithubUserRequest(accessToken)); memberRepository.findByUserId(userResponse.userId()) diff --git a/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java b/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java index f515f7b9f..30ffda460 100644 --- a/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java +++ b/backend/src/main/java/site/coduo/member/service/GithubOAuthService.java @@ -7,6 +7,7 @@ import site.coduo.member.client.GithubOAuthClient; import site.coduo.member.client.dto.TokenRequest; import site.coduo.member.client.dto.TokenResponse; +import site.coduo.member.infrastructure.security.JwtProvider; import site.coduo.member.infrastructure.security.NonceProvider; import site.coduo.member.service.dto.oauth.GithubAuthQuery; @@ -17,6 +18,7 @@ public class GithubOAuthService { private final GithubOAuthClient oAuthClient; private final NonceProvider nonceProvider; + private final JwtProvider jwtProvider; public GithubAuthQuery createAuthorizationContent() { @@ -27,8 +29,9 @@ public GithubAuthQuery createAuthorizationContent() { ); } - public TokenResponse invokeOAuthCallback(final String code) { + public String invokeOAuthCallback(final String code) { String redirectUri = oAuthClient.getOAuthRedirectUri(); - return oAuthClient.grant(new TokenRequest(code, redirectUri)); + final TokenResponse tokenResponse = oAuthClient.grant(new TokenRequest(code, redirectUri)); + return jwtProvider.sign(tokenResponse.accessToken()); } } diff --git a/backend/src/main/java/site/coduo/member/service/MemberService.java b/backend/src/main/java/site/coduo/member/service/MemberService.java index 6fc45d9ed..1212f6482 100644 --- a/backend/src/main/java/site/coduo/member/service/MemberService.java +++ b/backend/src/main/java/site/coduo/member/service/MemberService.java @@ -26,7 +26,8 @@ public class MemberService { private final JwtProvider jwtProvider; @Transactional - public void createMember(final String username, final String accessToken) { + public void createMember(final String username, final String encryptedAccessToken) { + final String accessToken = jwtProvider.extractSubject(encryptedAccessToken); final Bearer bearer = new Bearer(accessToken); final GithubUserResponse userResponse = githubClient.getUser(new GithubUserRequest(bearer)); final Member member = userResponse.toDomain(bearer, username); diff --git a/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java new file mode 100644 index 000000000..25da97087 --- /dev/null +++ b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java @@ -0,0 +1,28 @@ +package site.coduo.member.service.dto.auth; + +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; + +import java.time.Duration; + +import org.springframework.http.ResponseCookie; + +public record AccessTokenCookie(String accessToken) { + + public static ResponseCookie expire(final String domain) { + return ResponseCookie.from(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) + .maxAge(Duration.ZERO) + .domain(domain) + .path("/") + .build(); + } + + public ResponseCookie generate(final String domain) { + return ResponseCookie.from(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) + .value(accessToken) + .httpOnly(true) + .secure(true) + .domain(domain) + .path("/") + .build(); + } +} diff --git a/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java b/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java index 0e9fcc55c..099076c23 100644 --- a/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java +++ b/backend/src/main/java/site/coduo/member/service/dto/auth/SignInCookie.java @@ -8,19 +8,19 @@ public record SignInCookie(String credential) { - public ResponseCookie generate(final String domain) { + public static ResponseCookie expire(final String domain) { return ResponseCookie.from(SIGN_IN_COOKIE_NAME) - .value(credential) - .httpOnly(true) - .secure(true) + .maxAge(Duration.ZERO) .domain(domain) .path("/") .build(); } - public ResponseCookie expire(final String domain) { + public ResponseCookie generate(final String domain) { return ResponseCookie.from(SIGN_IN_COOKIE_NAME) - .maxAge(Duration.ZERO) + .value(credential) + .httpOnly(true) + .secure(true) .domain(domain) .path("/") .build(); diff --git a/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java index c68ccf203..3494df21a 100644 --- a/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/AuthAcceptanceTest.java @@ -2,6 +2,8 @@ import static org.hamcrest.Matchers.is; +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; + import java.util.Map; import org.apache.http.HttpStatus; @@ -28,7 +30,7 @@ class AuthAcceptanceTest extends AcceptanceFixture { @Test @DisplayName("로그인 검증 & 로그인 토큰을 발급한다.") void verify_login_and_publish_login_token() { - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String cookie = GithubAcceptanceTest.createAccessTokenCookie(); final Member member = createMember(); memberRepository.save(member); @@ -36,7 +38,7 @@ void verify_login_and_publish_login_token() { // when RestAssured .given() - .cookie("JSESSIONID", sessionId) + .cookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME, cookie) .when() .get("/api/sign-in/callback") @@ -50,12 +52,12 @@ void verify_login_and_publish_login_token() { @Test @DisplayName("로그인 검증 & 로그인 토큰을 발급한다. - 로그인 실패 케이스") void verify_login_and_publish_login_token_dose_not_exists_case() { - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String tempCookie = GithubAcceptanceTest.createAccessTokenCookie(); // when RestAssured .given() - .cookie("JSESSIONID", sessionId) + .cookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME, tempCookie) .when() .get("/api/sign-in/callback") @@ -107,7 +109,7 @@ void check_member_login_state() { @DisplayName("인가 정보를 통해 회원가입을 한다.") void sign_up_via_authorization_info() { // given - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String tempCookie = GithubAcceptanceTest.createAccessTokenCookie(); final Map body = Map.of("username", "닉네임"); // when & then @@ -115,7 +117,7 @@ void sign_up_via_authorization_info() { .given().log().all() .contentType(ContentType.JSON) .body(body) - .cookie("JSESSIONID", sessionId) + .cookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME, tempCookie) .when() .post("/api/sign-up") diff --git a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java index a71cc619d..350afd1eb 100644 --- a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java @@ -3,6 +3,8 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; + import java.util.Map; import org.apache.http.HttpStatus; @@ -18,16 +20,13 @@ class GithubAcceptanceTest extends AcceptanceFixture { - static String createAccessTokenThenReturnSessionId() { - final String session = callAuthorizeThenReturnSessionId(); - + static String createAccessTokenCookie() { final Map query = Map.of("code", "authorization code", "state", FixedNonceProvider.FIXED_VALUE); - RestAssured + return RestAssured .given() .queryParams(query) - .sessionId("JSESSIONID", session) .redirects() .follow(false) .log().all() @@ -35,23 +34,8 @@ static String createAccessTokenThenReturnSessionId() { .when() .get("/api/github/callback") - .then() - .statusCode(HttpStatus.SC_MOVED_TEMPORARILY); - - return session; - } - - static String callAuthorizeThenReturnSessionId() { - return RestAssured - .given() - .redirects() - .follow(false) - - .when() - .get("/api/sign-in/oauth/github") - .thenReturn() - .getSessionId(); + .getCookie(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME); } @Test @@ -92,8 +76,6 @@ void call_github_authorize_endpoint() { @DisplayName("callback 엔드포인트 호출") void call_callback_end_point() { // given - final String session = callAuthorizeThenReturnSessionId(); - final Map query = Map.of("code", "authorization code", "state", FixedNonceProvider.FIXED_VALUE); @@ -101,7 +83,6 @@ void call_callback_end_point() { RestAssured .given() .queryParams(query) - .sessionId("JSESSIONID", session) .redirects() .follow(false) .log().all() @@ -110,7 +91,7 @@ void call_callback_end_point() { .get("/api/github/callback") .then() - .statusCode(HttpStatus.SC_MOVED_TEMPORARILY); + .statusCode(HttpStatus.SC_TEMPORARY_REDIRECT); } @Test @@ -124,7 +105,6 @@ void try_login_when_call_callback_end_point() { .accessToken(FakeGithubOAuthClient.ACCESS_TOKEN.getCredential()) .profileImage(FakeGithubApiClient.PROFILE_IMAGE) .build(); - final String session = callAuthorizeThenReturnSessionId(); final Map query = Map.of("code", "authorization code", "state", FixedNonceProvider.FIXED_VALUE); @@ -134,7 +114,6 @@ void try_login_when_call_callback_end_point() { RestAssured .given() .queryParams(query) - .sessionId("JSESSIONID", session) .redirects() .follow(false) .log().all() @@ -143,6 +122,6 @@ void try_login_when_call_callback_end_point() { .get("/api/github/callback") .then().log().all() - .statusCode(302); + .statusCode(HttpStatus.SC_TEMPORARY_REDIRECT); } } diff --git a/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java index 7b3e6a44c..254e92445 100644 --- a/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java @@ -51,7 +51,7 @@ void search_member_info() { } String login(Member member) { - final String sessionId = GithubAcceptanceTest.createAccessTokenThenReturnSessionId(); + final String sessionId = GithubAcceptanceTest.createAccessTokenCookie(); memberRepository.save(member); diff --git a/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java b/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java index d049ba040..0081d33e4 100644 --- a/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/AuthServiceTest.java @@ -35,13 +35,14 @@ void tearDown() { } @Test - @DisplayName("엑세스 토큰으로 회원을 조회한다.") + @DisplayName("JWT로 감싸진 엑세스 토큰으로 회원을 조회한다.") void search_member_by_access_token() { // given final Member member = createMember("username", FakeGithubApiClient.ACCESS_TOKEN, FakeGithubApiClient.USER_ID); + final String sign = jwtProvider.sign(member.getAccessToken()); // when - final SignInServiceResponse signInToken = authService.createSignInToken(member.getAccessToken()); + final SignInServiceResponse signInToken = authService.createSignInToken(sign); // then assertThat(signInToken.signedIn()).isTrue(); @@ -52,9 +53,10 @@ void search_member_by_access_token() { void throw_exception_when_search_by_does_not_exists_access_token() { // given final String token = "does not exist token"; + final String sign = jwtProvider.sign(token); // when - final SignInServiceResponse signInToken = authService.createSignInToken(token); + final SignInServiceResponse signInToken = authService.createSignInToken(sign); // then assertThat(signInToken.token()).isEmpty(); @@ -77,9 +79,10 @@ private Member createMember(final String username, final String accessToken, fin void renewal_member_access_token_when_create_sign_in_token() { // given final Member member = createMember("username", "origin", FakeGithubApiClient.USER_ID); + final String sign = jwtProvider.sign("change"); // when - authService.createSignInToken("change"); + authService.createSignInToken(sign); // then assertThat(memberRepository.findById(member.getId()).orElseThrow()) diff --git a/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java b/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java index b2e7330d9..48be00a77 100644 --- a/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/GithubOAuthServiceTest.java @@ -1,6 +1,7 @@ package site.coduo.member.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,7 +12,7 @@ import site.coduo.config.TestConfig; import site.coduo.fake.FakeGithubOAuthClient; import site.coduo.fake.FixedNonceProvider; -import site.coduo.member.client.dto.TokenResponse; +import site.coduo.member.infrastructure.security.JwtProvider; import site.coduo.member.service.dto.oauth.GithubAuthQuery; @SpringBootTest @@ -21,6 +22,9 @@ class GithubOAuthServiceTest { @Autowired private GithubOAuthService githubOAuthService; + @Autowired + private JwtProvider jwtProvider; + @Test @DisplayName("인가 요청을 위한 정보를 생성한다.") void create_info_for_authorization_request_to_third_party() { @@ -45,9 +49,10 @@ void get_access_token() { final String code = "code"; // when - final TokenResponse tokenResponse = githubOAuthService.invokeOAuthCallback(code); + final String tempToken = githubOAuthService.invokeOAuthCallback(code); // then - assertThat(tokenResponse.accessToken()).isEqualTo(FakeGithubOAuthClient.ACCESS_TOKEN.getCredential()); + assertThatCode(() -> jwtProvider.extractSubject(tempToken)) + .doesNotThrowAnyException(); } } diff --git a/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java b/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java index 4a3ca5dbe..4dddba76c 100644 --- a/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java @@ -38,10 +38,11 @@ void tearDown() { void save_member() { // given final String credential = "access-token"; + final String token = jwtProvider.sign(credential); final String username = "username"; // when - memberService.createMember(username, credential); + memberService.createMember(username, token); // then assertThat(memberRepository.findAll()).hasSize(1); From 85bcf8a39197ee2fb13269820f3e9be79ca7bc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 20:49:32 +0900 Subject: [PATCH 40/74] =?UTF-8?q?[BE]=20=EC=BF=A0=ED=82=A4=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=84=9C=EB=B8=8C=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EA=B9=8C=EC=A7=80=20=ED=97=88=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20=20(#718)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 운영서버 배포 (#690) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 운영서버 배포 (#694) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#693) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 * [BE] 클라이언트 에러 로그 없애기 (#692) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#713) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * fix: 쿠키 서브 도메인까지 허용하도록 수정 * refactor: 개행 삭제 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --- .../java/site/coduo/member/controller/AuthController.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 9f99cc641..6438b10b6 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -5,7 +5,6 @@ import java.net.URI; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; @@ -31,14 +30,11 @@ @RestController public class AuthController implements AuthControllerDocs { - public static final String PRODUCT_DOMAIN = "coduo.site"; + public static final String PRODUCT_DOMAIN = ".coduo.site"; private final AuthService authService; private final MemberService memberService; - @Value("${front.url}") - private String frontUrl; - @GetMapping("/sign-out") public ResponseEntity signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) { final ResponseCookie expire = SignInCookie.expire(PRODUCT_DOMAIN); From 209ecda04a8f4dc59118d506200aeab3cfd29850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 20:53:31 +0900 Subject: [PATCH 41/74] =?UTF-8?q?[BE]=20=EC=BF=A0=ED=82=A4=20=EC=84=9C?= =?UTF-8?q?=EB=B8=8C=20=EB=8F=84=EB=A9=94=EC=9D=B8=EA=B0=84=20=EC=A0=84?= =?UTF-8?q?=EB=8B=AC=20=ED=97=88=EC=9A=A9=20=20(#719)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 * [BE] 쿠키 도메인 서브 도메인까지 허용하도록 수정 (#718) * [BE] 운영서버 배포 (#690) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 운영서버 배포 (#694) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#693) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 * [BE] 클라이언트 에러 로그 없애기 (#692) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#713) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * fix: 쿠키 서브 도메인까지 허용하도록 수정 * refactor: 개행 삭제 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone --- .../site/coduo/common/config/web/FilterConfig.java | 1 - .../coduo/member/controller/AuthController.java | 14 ++++++-------- .../member/controller/GithubOAuthController.java | 5 +++-- .../coduo/acceptance/GithubAcceptanceTest.java | 4 ++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java index a7af5ec93..0e1d3fed7 100644 --- a/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/FilterConfig.java @@ -20,7 +20,6 @@ public class FilterConfig { public FilterRegistrationBean accessTokenSessionFilter(final JwtProvider jwtProvider) { final FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setFilter(new AccessTokenCookieFilter(jwtProvider)); - bean.addUrlPatterns("/api/sign-up", "/api/sign-in/callback"); bean.setOrder(2); return bean; diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 4536c5156..37765f03a 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -5,7 +5,6 @@ import java.net.URI; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; @@ -31,16 +30,14 @@ @RestController public class AuthController implements AuthControllerDocs { + public static final String PRODUCT_DOMAIN = ".coduo.site"; + private final AuthService authService; private final MemberService memberService; - @Value("${front.url}") - private String frontUrl; - @GetMapping("/sign-out") public ResponseEntity signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) { - final ResponseCookie expire = SignInCookie.expire(frontUrl); - + final ResponseCookie expire = SignInCookie.expire(PRODUCT_DOMAIN); return ResponseEntity.ok() .header(HttpHeaders.SET_COOKIE, expire.toString()) .build(); @@ -62,10 +59,11 @@ public ResponseEntity signInCallback( @CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken ) { final SignInServiceResponse serviceResponse = authService.createSignInToken(encryptedAccessToken); - final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(frontUrl); + final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(PRODUCT_DOMAIN); + final ResponseCookie expireTemporaryAccessToken = AccessTokenCookie.expire(PRODUCT_DOMAIN); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), AccessTokenCookie.expire(frontUrl).toString()) + .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), expireTemporaryAccessToken.toString()) .body(SignInWebResponse.of(serviceResponse)); } diff --git a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java index 7471cb93f..8319d7ce4 100644 --- a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java @@ -3,6 +3,7 @@ import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND; import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME; +import static site.coduo.member.controller.AuthController.PRODUCT_DOMAIN; import java.net.URI; @@ -53,9 +54,9 @@ public ResponseEntity getAccessToken(@ModelAttribute final GithubCallbackQ final HttpSession session) { final String encryptedAccessToken = githubOAuthService.invokeOAuthCallback(query.code()); final AccessTokenCookie cookie = new AccessTokenCookie(encryptedAccessToken); - final ResponseCookie responseCookie = cookie.generate(frontUrl); + final ResponseCookie responseCookie = cookie.generate(PRODUCT_DOMAIN); - return ResponseEntity.status(HttpStatus.FOUND) + return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT) .header(HttpHeaders.SET_COOKIE, responseCookie.toString()) .location(URI.create("https://" + frontUrl + "/callback")) .build(); diff --git a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java index 72866994f..350afd1eb 100644 --- a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java @@ -91,7 +91,7 @@ void call_callback_end_point() { .get("/api/github/callback") .then() - .statusCode(HttpStatus.SC_MOVED_TEMPORARILY); + .statusCode(HttpStatus.SC_TEMPORARY_REDIRECT); } @Test @@ -122,6 +122,6 @@ void try_login_when_call_callback_end_point() { .get("/api/github/callback") .then().log().all() - .statusCode(302); + .statusCode(HttpStatus.SC_TEMPORARY_REDIRECT); } } From 62d6647bb685fe438a23e6327044c86e8a254b68 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 7 Oct 2024 20:57:26 +0900 Subject: [PATCH 42/74] =?UTF-8?q?feat:=20=ED=8E=98=EC=96=B4=EB=A3=B8=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coduo/pairroom/controller/PairRoomController.java | 10 ++++++++++ .../site/coduo/pairroom/service/PairRoomService.java | 4 ++++ .../pairroom/service/dto/PairRoomExistResponse.java | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomExistResponse.java diff --git a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java index 4ced45960..8b01cb401 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import lombok.RequiredArgsConstructor; @@ -22,6 +23,7 @@ import site.coduo.pairroom.service.PairRoomService; import site.coduo.pairroom.service.dto.PairRoomCreateRequest; import site.coduo.pairroom.service.dto.PairRoomCreateResponse; +import site.coduo.pairroom.service.dto.PairRoomExistResponse; import site.coduo.pairroom.service.dto.PairRoomMemberResponse; import site.coduo.pairroom.service.dto.PairRoomReadRequest; import site.coduo.pairroom.service.dto.PairRoomReadResponse; @@ -82,4 +84,12 @@ public ResponseEntity> getPairRooms( return ResponseEntity.ok() .body(pairRooms); } + + @GetMapping("/pair-room/exist") + public ResponseEntity pairRoomExist(@RequestParam final String accessCode) { + final PairRoomExistResponse response = new PairRoomExistResponse( + pairRoomService.existsByAccessCode(accessCode)); + + return ResponseEntity.ok(response); + } } diff --git a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java index 1f6c0ec00..bec2afc18 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java +++ b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java @@ -55,6 +55,10 @@ public String savePairRoom(final PairRoomCreateRequest request, @Nullable final return pairRoom.getAccessCodeText(); } + public boolean existsByAccessCode(final String accessCode) { + return pairRoomRepository.existsByAccessCode(accessCode); + } + private PairRoom createPairRoom(final PairRoomCreateRequest request) { final AccessCode accessCode = generateAccessCode(); final PairRoomStatus status = PairRoomStatus.findByName(request.status()); diff --git a/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomExistResponse.java b/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomExistResponse.java new file mode 100644 index 000000000..ccbb5968f --- /dev/null +++ b/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomExistResponse.java @@ -0,0 +1,7 @@ +package site.coduo.pairroom.service.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "페어룸 존재 여부 확인 응답 바디") +public record PairRoomExistResponse(@Schema(description = "페어룸 존재 확인") boolean exists) { +} From 2e67060bb0faad4fb681b4bdb0124bf75fbe0634 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 7 Oct 2024 20:57:40 +0900 Subject: [PATCH 43/74] =?UTF-8?q?docs:=20=ED=8E=98=EC=96=B4=EB=A3=B8=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?API=20=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../site/coduo/pairroom/controller/docs/PairRoomDocs.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java index 9163e35f2..91a46218b 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java @@ -14,10 +14,11 @@ import io.swagger.v3.oas.annotations.tags.Tag; import site.coduo.pairroom.service.dto.PairRoomCreateRequest; import site.coduo.pairroom.service.dto.PairRoomCreateResponse; +import site.coduo.pairroom.service.dto.PairRoomExistResponse; +import site.coduo.pairroom.service.dto.PairRoomMemberResponse; import site.coduo.pairroom.service.dto.PairRoomReadRequest; import site.coduo.pairroom.service.dto.PairRoomReadResponse; import site.coduo.pairroom.service.dto.PairRoomStatusUpdateRequest; -import site.coduo.pairroom.service.dto.PairRoomMemberResponse; @Tag(name = "페어룸 API") public interface PairRoomDocs { @@ -68,4 +69,9 @@ ResponseEntity> getPairRooms( required = true ) String signInToken); + + @Operation(summary = "액세스 코드로 페어룸이 존재하는지 조회한다.") + @ApiResponse(responseCode = "200", description = "페어룸 존재 여부", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = PairRoomExistResponse.class))) + ResponseEntity pairRoomExist(String accessCode); } From d794cfe8cfd3f2e98773513781cdf48712a2b24e Mon Sep 17 00:00:00 2001 From: anttiey Date: Mon, 7 Oct 2024 21:02:14 +0900 Subject: [PATCH 44/74] =?UTF-8?q?:ambulance:=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=EC=97=90=20=EC=BD=98=EC=86=94=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.tsx | 3 ++- frontend/src/pages/Callback/Callback.tsx | 11 +++++++++++ frontend/src/pages/Main/Main.tsx | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8ea7cfdf8..226fc4dd4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -36,7 +36,8 @@ const App = () => { const { signedIn } = await getIsUserLoggedIn(); if (!signedIn) { - return setUser('', 'SIGNED_OUT'); + setUser('', 'SIGNED_OUT'); + return; } const { username } = await getMember(); diff --git a/frontend/src/pages/Callback/Callback.tsx b/frontend/src/pages/Callback/Callback.tsx index 3db4f9107..41c5e1c55 100644 --- a/frontend/src/pages/Callback/Callback.tsx +++ b/frontend/src/pages/Callback/Callback.tsx @@ -19,17 +19,28 @@ const Callback = () => { useEffect(() => { const handleCallBack = async () => { + console.log('1'); + const { signedUp } = await getSignInCallback(); + console.log('2'); + if (signedUp) { // const { username } = await getMember(); // setUser(username, 'SIGNED_IN'); + + console.log('3'); + navigate('/main'); + console.log('4'); + return; } + console.log('5'); + navigate('/sign-up'); }; diff --git a/frontend/src/pages/Main/Main.tsx b/frontend/src/pages/Main/Main.tsx index fc9db0726..f0181ac4c 100644 --- a/frontend/src/pages/Main/Main.tsx +++ b/frontend/src/pages/Main/Main.tsx @@ -10,6 +10,8 @@ import useModal from '@/hooks/common/useModal'; import usePreventBackNavigation from '@/hooks/common/usePreventBackNavigation'; const Main = () => { + console.log('6'); + usePreventBackNavigation(); const { From 88dbe48f22527c06dc12fdf5624f6d9847bf0aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 7 Oct 2024 21:08:32 +0900 Subject: [PATCH 45/74] =?UTF-8?q?[BE]=20=EC=84=B8=EC=85=98=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20=EC=BF=A0=ED=82=A4=EB=A1=9C=20=EB=8C=80=EC=B2=B4=20?= =?UTF-8?q?(#721)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 세션 사용을 쿠키로 대체 (#706) (#711) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 서브 도메인간 전달 허용 (#719) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 * [BE] 쿠키 도메인 서브 도메인까지 허용하도록 수정 (#718) * [BE] 운영서버 배포 (#690) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 운영서버 배포 (#694) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#693) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 * [BE] 클라이언트 에러 로그 없애기 (#692) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#713) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * fix: 쿠키 서브 도메인까지 허용하도록 수정 * refactor: 개행 삭제 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone --- .../java/site/coduo/member/controller/AuthController.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 9f99cc641..6438b10b6 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -5,7 +5,6 @@ import java.net.URI; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; @@ -31,14 +30,11 @@ @RestController public class AuthController implements AuthControllerDocs { - public static final String PRODUCT_DOMAIN = "coduo.site"; + public static final String PRODUCT_DOMAIN = ".coduo.site"; private final AuthService authService; private final MemberService memberService; - @Value("${front.url}") - private String frontUrl; - @GetMapping("/sign-out") public ResponseEntity signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) { final ResponseCookie expire = SignInCookie.expire(PRODUCT_DOMAIN); From 811b4f0913e7e9419e54d7922885b73a6fd76369 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 7 Oct 2024 21:24:05 +0900 Subject: [PATCH 46/74] =?UTF-8?q?test:=20=ED=8E=98=EC=96=B4=EB=A3=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PairRoomController.java | 2 +- .../acceptance/PairRoomAcceptanceTest.java | 57 ++++++++++++++++++- .../pairroom/service/PairRoomServiceTest.java | 21 +++++++ 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java index 8b01cb401..093deb972 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java @@ -86,7 +86,7 @@ public ResponseEntity> getPairRooms( } @GetMapping("/pair-room/exist") - public ResponseEntity pairRoomExist(@RequestParam final String accessCode) { + public ResponseEntity pairRoomExist(@RequestParam("access-code") final String accessCode) { final PairRoomExistResponse response = new PairRoomExistResponse( pairRoomService.existsByAccessCode(accessCode)); diff --git a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java index b68cd09bb..eabe684f4 100644 --- a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java @@ -1,19 +1,20 @@ package site.coduo.acceptance; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; -import org.springframework.transaction.annotation.Transactional; import io.restassured.RestAssured; import io.restassured.http.ContentType; import site.coduo.pairroom.domain.PairRoomStatus; import site.coduo.pairroom.service.dto.PairRoomCreateRequest; import site.coduo.pairroom.service.dto.PairRoomCreateResponse; +import site.coduo.pairroom.service.dto.PairRoomExistResponse; -@Transactional class PairRoomAcceptanceTest extends AcceptanceFixture { static PairRoomCreateResponse createPairRoom(final PairRoomCreateRequest request) { @@ -98,4 +99,56 @@ void update_driver_navigator() { .then() .statusCode(204); } + + @Test + @DisplayName("페어룸이 존재하면 true를 반환한다.") + void exist_pair_room_true() { + //given + final PairRoomCreateResponse accessCode = + createPairRoom(new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "IN_PROGRESS")); + + // when & then + final PairRoomExistResponse response = RestAssured + .given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON_VALUE) + .log() + .all() + + .when() + .queryParam("access-code", accessCode.accessCode()) + .get("/api/pair-room/exist") + + .then() + .statusCode(200) + .extract() + .as(PairRoomExistResponse.class); + + assertThat(response.exists()).isTrue(); + } + + @Test + @DisplayName("페어룸이 존재하면 false를 반환한다.") + void exist_pair_room_false() { + //given + + // when & then + final PairRoomExistResponse response = RestAssured + .given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON_VALUE) + .log() + .all() + + .when() + .queryParam("access-code", "babyroom") + .get("/api/pair-room/exist") + + .then() + .statusCode(200) + .extract() + .as(PairRoomExistResponse.class); + + assertThat(response.exists()).isFalse(); + } } diff --git a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java index 6bfa85d9e..b02c50705 100644 --- a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java +++ b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.junit.jupiter.api.Assertions.assertAll; import java.util.List; import java.util.Random; @@ -201,4 +202,24 @@ void get_pair_room_and_timer() { .contains(pairRoomEntity.getNavigator(), pairRoomEntity.getDriver(), pairRoomEntity.getStatus().toString(), timer.getDuration(), timer.getRemainingTime()); } + + @Test + @DisplayName("페어룸이 존재하는지 확인한다.") + void exists_pair_room() { + //given + final AccessCode accessCode = new AccessCode("123456"); + final PairRoomEntity pairRoomEntity = PairRoomEntity.from( + new PairRoom(PairRoomStatus.IN_PROGRESS, + new Pair(new PairName("레디"), new PairName("레모네")), + accessCode + )); + pairRoomRepository.save(pairRoomEntity); + + //when & then + assertAll( + () -> assertThat(pairRoomService.existsByAccessCode("not-exist")).isFalse(), + () -> assertThat(pairRoomService.existsByAccessCode(accessCode.getValue())).isTrue() + + ); + } } From 29f39564f1624815cbd370585b3c8321a3b7678b Mon Sep 17 00:00:00 2001 From: anttiey Date: Mon, 7 Oct 2024 21:32:08 +0900 Subject: [PATCH 47/74] =?UTF-8?q?:ambulance:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EC=BD=9C=EB=B0=B1=20=EB=B2=84=EA=B7=B8=20=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.tsx | 2 +- frontend/src/pages/Callback/Callback.tsx | 21 +++++---------------- frontend/src/pages/Main/Main.tsx | 2 -- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 226fc4dd4..6b2bed03d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -46,7 +46,7 @@ const App = () => { }; useEffect(() => { - if (process.env.NODE_ENV === 'production') updateUser(); + if (window.location.pathname !== '/callback') updateUser(); }, []); const router = createBrowserRouter([ diff --git a/frontend/src/pages/Callback/Callback.tsx b/frontend/src/pages/Callback/Callback.tsx index 41c5e1c55..eb7639903 100644 --- a/frontend/src/pages/Callback/Callback.tsx +++ b/frontend/src/pages/Callback/Callback.tsx @@ -5,9 +5,9 @@ import { LogoIconWithTitle } from '@/assets'; import Spinner from '@/components/common/Spinner/Spinner'; -// import useUserStore from '@/stores/userStore'; +import useUserStore from '@/stores/userStore'; -// import { getMember } from '@/apis/member'; +import { getMember } from '@/apis/member'; import { getSignInCallback } from '@/apis/oauth'; import * as S from './Callback.styles'; @@ -15,32 +15,21 @@ import * as S from './Callback.styles'; const Callback = () => { const navigate = useNavigate(); - // const { setUser } = useUserStore(); + const { setUser } = useUserStore(); useEffect(() => { const handleCallBack = async () => { - console.log('1'); - const { signedUp } = await getSignInCallback(); - console.log('2'); - if (signedUp) { - // const { username } = await getMember(); - - // setUser(username, 'SIGNED_IN'); - - console.log('3'); + const { username } = await getMember(); + setUser(username, 'SIGNED_IN'); navigate('/main'); - console.log('4'); - return; } - console.log('5'); - navigate('/sign-up'); }; diff --git a/frontend/src/pages/Main/Main.tsx b/frontend/src/pages/Main/Main.tsx index f0181ac4c..fc9db0726 100644 --- a/frontend/src/pages/Main/Main.tsx +++ b/frontend/src/pages/Main/Main.tsx @@ -10,8 +10,6 @@ import useModal from '@/hooks/common/useModal'; import usePreventBackNavigation from '@/hooks/common/usePreventBackNavigation'; const Main = () => { - console.log('6'); - usePreventBackNavigation(); const { From 87f2d0c2848e29bd1fbe58ee79318b3e5f466b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Tue, 8 Oct 2024 10:14:04 +0900 Subject: [PATCH 48/74] =?UTF-8?q?[BE]=20=EB=A9=A4=EB=B2=84=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=A0=9C=EC=95=BD=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#724)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 멤버 테이블 제약조건 추가 * fix: 임시 쿠키 명시적 만료 제거 --- .../java/site/coduo/member/controller/AuthController.java | 4 +--- backend/src/main/java/site/coduo/member/domain/Member.java | 4 ++-- .../site/coduo/member/service/dto/auth/AccessTokenCookie.java | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 37765f03a..3451221e4 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -20,7 +20,6 @@ import site.coduo.member.service.AuthService; import site.coduo.member.service.MemberService; import site.coduo.member.service.dto.SignInServiceResponse; -import site.coduo.member.service.dto.auth.AccessTokenCookie; import site.coduo.member.service.dto.auth.SignInCheckResponse; import site.coduo.member.service.dto.auth.SignInCookie; import site.coduo.member.service.dto.auth.SignInWebResponse; @@ -60,10 +59,9 @@ public ResponseEntity signInCallback( ) { final SignInServiceResponse serviceResponse = authService.createSignInToken(encryptedAccessToken); final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(PRODUCT_DOMAIN); - final ResponseCookie expireTemporaryAccessToken = AccessTokenCookie.expire(PRODUCT_DOMAIN); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), expireTemporaryAccessToken.toString()) + .header(HttpHeaders.SET_COOKIE, signInCookie.toString()) .body(SignInWebResponse.of(serviceResponse)); } diff --git a/backend/src/main/java/site/coduo/member/domain/Member.java b/backend/src/main/java/site/coduo/member/domain/Member.java index 850d85af2..fe3888c33 100644 --- a/backend/src/main/java/site/coduo/member/domain/Member.java +++ b/backend/src/main/java/site/coduo/member/domain/Member.java @@ -26,13 +26,13 @@ public class Member extends BaseTimeEntity { @Column(name = "ID", nullable = false) private Long id; - @Column(name = "ACCESS_TOKEN", nullable = false) + @Column(name = "ACCESS_TOKEN", nullable = false, unique = true) private String accessToken; @Column(name = "PROVIDER_LOGIN_ID", nullable = false) private String loginId; - @Column(name = "PROVIDER_USER_ID", nullable = false) + @Column(name = "PROVIDER_USER_ID", nullable = false, unique = true) private String userId; @Column(name = "PROFILE_IMAGE") diff --git a/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java index 25da97087..f3313c99e 100644 --- a/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java +++ b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java @@ -19,6 +19,7 @@ public static ResponseCookie expire(final String domain) { public ResponseCookie generate(final String domain) { return ResponseCookie.from(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) .value(accessToken) + .maxAge(Duration.ofMinutes(10)) .httpOnly(true) .secure(true) .domain(domain) From 0cc1a4f7ab8aeec4e459c8b8a82b19e13d0e397f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Tue, 8 Oct 2024 10:38:51 +0900 Subject: [PATCH 49/74] =?UTF-8?q?[BE]=20=EC=A1=B0=EC=95=BD=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=BF=A0=ED=82=A4?= =?UTF-8?q?=20=EB=AA=85=EC=8B=9C=EC=A0=81=20=EC=82=AD=EC=A0=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=20(#725)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 * [BE] 쿠키 도메인 서브 도메인까지 허용하도록 수정 (#718) * [BE] 운영서버 배포 (#690) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 운영서버 배포 (#694) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#693) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 * [BE] 클라이언트 에러 로그 없애기 (#692) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#713) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * fix: 쿠키 서브 도메인까지 허용하도록 수정 * refactor: 개행 삭제 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 멤버 테이블 제약조건 추가 (#724) * fix: 멤버 테이블 제약조건 추가 * fix: 임시 쿠키 명시적 만료 제거 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone --- .../java/site/coduo/member/controller/AuthController.java | 5 +---- backend/src/main/java/site/coduo/member/domain/Member.java | 4 ++-- .../coduo/member/service/dto/auth/AccessTokenCookie.java | 1 + 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 6438b10b6..3451221e4 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -20,7 +20,6 @@ import site.coduo.member.service.AuthService; import site.coduo.member.service.MemberService; import site.coduo.member.service.dto.SignInServiceResponse; -import site.coduo.member.service.dto.auth.AccessTokenCookie; import site.coduo.member.service.dto.auth.SignInCheckResponse; import site.coduo.member.service.dto.auth.SignInCookie; import site.coduo.member.service.dto.auth.SignInWebResponse; @@ -38,7 +37,6 @@ public class AuthController implements AuthControllerDocs { @GetMapping("/sign-out") public ResponseEntity signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) { final ResponseCookie expire = SignInCookie.expire(PRODUCT_DOMAIN); - return ResponseEntity.ok() .header(HttpHeaders.SET_COOKIE, expire.toString()) .build(); @@ -61,10 +59,9 @@ public ResponseEntity signInCallback( ) { final SignInServiceResponse serviceResponse = authService.createSignInToken(encryptedAccessToken); final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(PRODUCT_DOMAIN); - final ResponseCookie expireTemporaryAccessToken = AccessTokenCookie.expire(PRODUCT_DOMAIN); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), expireTemporaryAccessToken.toString()) + .header(HttpHeaders.SET_COOKIE, signInCookie.toString()) .body(SignInWebResponse.of(serviceResponse)); } diff --git a/backend/src/main/java/site/coduo/member/domain/Member.java b/backend/src/main/java/site/coduo/member/domain/Member.java index 850d85af2..fe3888c33 100644 --- a/backend/src/main/java/site/coduo/member/domain/Member.java +++ b/backend/src/main/java/site/coduo/member/domain/Member.java @@ -26,13 +26,13 @@ public class Member extends BaseTimeEntity { @Column(name = "ID", nullable = false) private Long id; - @Column(name = "ACCESS_TOKEN", nullable = false) + @Column(name = "ACCESS_TOKEN", nullable = false, unique = true) private String accessToken; @Column(name = "PROVIDER_LOGIN_ID", nullable = false) private String loginId; - @Column(name = "PROVIDER_USER_ID", nullable = false) + @Column(name = "PROVIDER_USER_ID", nullable = false, unique = true) private String userId; @Column(name = "PROFILE_IMAGE") diff --git a/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java index 25da97087..f3313c99e 100644 --- a/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java +++ b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java @@ -19,6 +19,7 @@ public static ResponseCookie expire(final String domain) { public ResponseCookie generate(final String domain) { return ResponseCookie.from(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) .value(accessToken) + .maxAge(Duration.ofMinutes(10)) .httpOnly(true) .secure(true) .domain(domain) From 7fd872d5e88c5c3e33258186c961901647498381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Tue, 8 Oct 2024 11:10:13 +0900 Subject: [PATCH 50/74] =?UTF-8?q?[BE]=20=EC=8A=AC=EB=9E=99=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EA=B8=B0=EB=8A=A5=20=EC=98=A4=EB=A5=98=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20(#727)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FE 브랜치 pull 받으면서 생긴 장애 수정 --- .github/workflows/pull_request.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index fb60fecc3..bd43b5cd0 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -1,4 +1,4 @@ -name: PULL REQUEST +name: pull request on: pull_request: @@ -11,7 +11,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - ref: ${{ vars.SLACK_BRANCH}} + ref: ${{vars.SLACK_BRANCH}} - name: Install jq run: sudo apt-get install jq @@ -43,7 +43,7 @@ jobs: id: pull_request_notify uses: slackapi/slack-github-action@v1.26.0 with: - channel-id: ${{secrets.SLACK_FE_REVIEW_CHANNEL}} + channel-id: ${{secrets.SLACK_BE_REVIEW_CHANNEL}} payload: | { "text": "⌛PR왔다 리뷰해라⌛", From 36d90c8a413c62338cfdd0cc8829628e08eb6339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Tue, 8 Oct 2024 11:20:49 +0900 Subject: [PATCH 51/74] =?UTF-8?q?[BE]=20=EC=9A=B4=EC=98=81=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=EB=B0=B0=ED=8F=AC=20(#726)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 세션 사용을 쿠키로 대체 (#706) (#711) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 * [BE] 쿠키 도메인 서브 도메인까지 허용하도록 수정 (#718) * [BE] 운영서버 배포 (#690) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 운영서버 배포 (#694) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#693) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 * [BE] 클라이언트 에러 로그 없애기 (#692) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#713) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * fix: 쿠키 서브 도메인까지 허용하도록 수정 * refactor: 개행 삭제 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 서브 도메인간 전달 허용 (#719) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 * [BE] 쿠키 도메인 서브 도메인까지 허용하도록 수정 (#718) * [BE] 운영서버 배포 (#690) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 운영서버 배포 (#694) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#693) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 * [BE] 클라이언트 에러 로그 없애기 (#692) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#713) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * fix: 쿠키 서브 도메인까지 허용하도록 수정 * refactor: 개행 삭제 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone * feat: 페어룸 존재 여부 확인 API 추가 * docs: 페어룸 존재 여부 확인 API 문서 추가 * test: 페어룸 조회 테스트 추가 * [BE] 멤버 테이블 제약조건 추가 (#724) * fix: 멤버 테이블 제약조건 추가 * fix: 임시 쿠키 명시적 만료 제거 * [BE] 조약조건 추가 및 쿠키 명시적 삭제 제거 (#725) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 * [BE] 쿠키 도메인 서브 도메인까지 허용하도록 수정 (#718) * [BE] 운영서버 배포 (#690) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 운영서버 배포 (#694) * [BE] 데이터소스 관련 환경변수 분리 (#663) (#664) * refactor: todo patch시 pair_room_id 사라지는 문제 해결 * fix: jwt NPE 핸들링 * fix: Sgin in NPE 핸들링 * fix: alllow header에 content tpye 추가 * fix: Timer update 테스트 오류 수정 * chore: 로그 심기 * fix: preflight 옵션 filter 제외 * chore: 로그 뽑기 * fix: 로그인 체크 필터링 제거 * feat: 데이터 소스 분리 * chore: 데이터 소스 CD/TEST 분리 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경 * chore: 데이터 소스 분리로 인한 컴포즈 변경1 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) * chore: be cd script 변경 * feat: 엑세스 코드 생성 전략 수정 (#670) * feat: 엑세스 코드 생성 전략 수정 (#670) (#671) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/dev (#673) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * Be/feature/pray (#674) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * refactor: member entity accessToken 컬럼 크기를 1000으로 변경 * refactor: pairroom entity accessToken 컬럼 크기를 1000으로 변경 * fix: entity 설정 원복 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * [BE] 스케줄러 로깅 문제 보완 (#679) * feat: 엑세스 코드 생성 전략 수정 (#670) * Be/feature/pray (#672) * feat: 엑세스 코드 생성 전략 수정 * feat: 엑세스 코드 생성 전략 수정 * feat: 스켈줄러 매치 에러 보완 (#678) * style: 로그 제거 * refactor: check 시 쿠키 값 필수 조건 해제 * [BE] TEST DB 소스 변경 (#686) (#687) * [BE] 테스트 서버 배포 (#689) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#693) * style: 로그 제거 * [BE] TEST DB 소스 변경 (#686) * refactor: stop 및 pause 메서드 리팩터링 * feat: 타이머가 실행 중에 다시 실행될 수 없게 조건문 추가 * feat: 타이머가 실행 중일 때만 종료할 수 있게 조건 추가 * refactor: TimerEntity 찾을 때 pairRoom id 대신 PairRoomEntity로 넘기기 * [BE] 클라이언트 에러 로그 없애기 (#692) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 --------- Co-authored-by: reddevilmidzy Co-authored-by: lemone Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 테스트 서버 배포 (#713) * [BE] 세션 사용을 쿠키로 대체 (#706) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 쿠키 도메인 설정 통일화 (#712) * feat: 스켈줄러 매치 에러 보완 * fix: 로그인 확인 로직 필터링 제외 * feat: 세션 사용 필터 삭제 * feat: 세션을 쿠키로 대체 * feat: 쿠키 도메인 설정 통일 * test: 상태 코드 수정 --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * fix: 쿠키 서브 도메인까지 허용하도록 수정 * refactor: 개행 삭제 --------- Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> * [BE] 멤버 테이블 제약조건 추가 (#724) * fix: 멤버 테이블 제약조건 추가 * fix: 임시 쿠키 명시적 만료 제거 --------- Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone * [BE] 슬랙 알림 기능 오류 개선 (#727) FE 브랜치 pull 받으면서 생긴 장애 수정 --------- Co-authored-by: Sunguk Yang (Kelly) Co-authored-by: lemone <55480011+JiHyeonL@users.noreply.github.com> Co-authored-by: reddevilmidzy Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Co-authored-by: lemone --- .github/workflows/pull_request.yml | 6 +- .../member/controller/AuthController.java | 5 +- .../java/site/coduo/member/domain/Member.java | 4 +- .../service/dto/auth/AccessTokenCookie.java | 1 + .../controller/PairRoomController.java | 10 ++++ .../controller/docs/PairRoomDocs.java | 8 ++- .../pairroom/service/PairRoomService.java | 4 ++ .../service/dto/PairRoomExistResponse.java | 7 +++ .../acceptance/PairRoomAcceptanceTest.java | 57 ++++++++++++++++++- .../pairroom/service/PairRoomServiceTest.java | 21 +++++++ 10 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomExistResponse.java diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index fb60fecc3..bd43b5cd0 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -1,4 +1,4 @@ -name: PULL REQUEST +name: pull request on: pull_request: @@ -11,7 +11,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - ref: ${{ vars.SLACK_BRANCH}} + ref: ${{vars.SLACK_BRANCH}} - name: Install jq run: sudo apt-get install jq @@ -43,7 +43,7 @@ jobs: id: pull_request_notify uses: slackapi/slack-github-action@v1.26.0 with: - channel-id: ${{secrets.SLACK_FE_REVIEW_CHANNEL}} + channel-id: ${{secrets.SLACK_BE_REVIEW_CHANNEL}} payload: | { "text": "⌛PR왔다 리뷰해라⌛", diff --git a/backend/src/main/java/site/coduo/member/controller/AuthController.java b/backend/src/main/java/site/coduo/member/controller/AuthController.java index 6438b10b6..3451221e4 100644 --- a/backend/src/main/java/site/coduo/member/controller/AuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/AuthController.java @@ -20,7 +20,6 @@ import site.coduo.member.service.AuthService; import site.coduo.member.service.MemberService; import site.coduo.member.service.dto.SignInServiceResponse; -import site.coduo.member.service.dto.auth.AccessTokenCookie; import site.coduo.member.service.dto.auth.SignInCheckResponse; import site.coduo.member.service.dto.auth.SignInCookie; import site.coduo.member.service.dto.auth.SignInWebResponse; @@ -38,7 +37,6 @@ public class AuthController implements AuthControllerDocs { @GetMapping("/sign-out") public ResponseEntity signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) { final ResponseCookie expire = SignInCookie.expire(PRODUCT_DOMAIN); - return ResponseEntity.ok() .header(HttpHeaders.SET_COOKIE, expire.toString()) .build(); @@ -61,10 +59,9 @@ public ResponseEntity signInCallback( ) { final SignInServiceResponse serviceResponse = authService.createSignInToken(encryptedAccessToken); final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(PRODUCT_DOMAIN); - final ResponseCookie expireTemporaryAccessToken = AccessTokenCookie.expire(PRODUCT_DOMAIN); return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, signInCookie.toString(), expireTemporaryAccessToken.toString()) + .header(HttpHeaders.SET_COOKIE, signInCookie.toString()) .body(SignInWebResponse.of(serviceResponse)); } diff --git a/backend/src/main/java/site/coduo/member/domain/Member.java b/backend/src/main/java/site/coduo/member/domain/Member.java index 850d85af2..fe3888c33 100644 --- a/backend/src/main/java/site/coduo/member/domain/Member.java +++ b/backend/src/main/java/site/coduo/member/domain/Member.java @@ -26,13 +26,13 @@ public class Member extends BaseTimeEntity { @Column(name = "ID", nullable = false) private Long id; - @Column(name = "ACCESS_TOKEN", nullable = false) + @Column(name = "ACCESS_TOKEN", nullable = false, unique = true) private String accessToken; @Column(name = "PROVIDER_LOGIN_ID", nullable = false) private String loginId; - @Column(name = "PROVIDER_USER_ID", nullable = false) + @Column(name = "PROVIDER_USER_ID", nullable = false, unique = true) private String userId; @Column(name = "PROFILE_IMAGE") diff --git a/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java index 25da97087..f3313c99e 100644 --- a/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java +++ b/backend/src/main/java/site/coduo/member/service/dto/auth/AccessTokenCookie.java @@ -19,6 +19,7 @@ public static ResponseCookie expire(final String domain) { public ResponseCookie generate(final String domain) { return ResponseCookie.from(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) .value(accessToken) + .maxAge(Duration.ofMinutes(10)) .httpOnly(true) .secure(true) .domain(domain) diff --git a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java index 4ced45960..093deb972 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import lombok.RequiredArgsConstructor; @@ -22,6 +23,7 @@ import site.coduo.pairroom.service.PairRoomService; import site.coduo.pairroom.service.dto.PairRoomCreateRequest; import site.coduo.pairroom.service.dto.PairRoomCreateResponse; +import site.coduo.pairroom.service.dto.PairRoomExistResponse; import site.coduo.pairroom.service.dto.PairRoomMemberResponse; import site.coduo.pairroom.service.dto.PairRoomReadRequest; import site.coduo.pairroom.service.dto.PairRoomReadResponse; @@ -82,4 +84,12 @@ public ResponseEntity> getPairRooms( return ResponseEntity.ok() .body(pairRooms); } + + @GetMapping("/pair-room/exist") + public ResponseEntity pairRoomExist(@RequestParam("access-code") final String accessCode) { + final PairRoomExistResponse response = new PairRoomExistResponse( + pairRoomService.existsByAccessCode(accessCode)); + + return ResponseEntity.ok(response); + } } diff --git a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java index 9163e35f2..91a46218b 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java @@ -14,10 +14,11 @@ import io.swagger.v3.oas.annotations.tags.Tag; import site.coduo.pairroom.service.dto.PairRoomCreateRequest; import site.coduo.pairroom.service.dto.PairRoomCreateResponse; +import site.coduo.pairroom.service.dto.PairRoomExistResponse; +import site.coduo.pairroom.service.dto.PairRoomMemberResponse; import site.coduo.pairroom.service.dto.PairRoomReadRequest; import site.coduo.pairroom.service.dto.PairRoomReadResponse; import site.coduo.pairroom.service.dto.PairRoomStatusUpdateRequest; -import site.coduo.pairroom.service.dto.PairRoomMemberResponse; @Tag(name = "페어룸 API") public interface PairRoomDocs { @@ -68,4 +69,9 @@ ResponseEntity> getPairRooms( required = true ) String signInToken); + + @Operation(summary = "액세스 코드로 페어룸이 존재하는지 조회한다.") + @ApiResponse(responseCode = "200", description = "페어룸 존재 여부", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = PairRoomExistResponse.class))) + ResponseEntity pairRoomExist(String accessCode); } diff --git a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java index 1f6c0ec00..bec2afc18 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java +++ b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java @@ -55,6 +55,10 @@ public String savePairRoom(final PairRoomCreateRequest request, @Nullable final return pairRoom.getAccessCodeText(); } + public boolean existsByAccessCode(final String accessCode) { + return pairRoomRepository.existsByAccessCode(accessCode); + } + private PairRoom createPairRoom(final PairRoomCreateRequest request) { final AccessCode accessCode = generateAccessCode(); final PairRoomStatus status = PairRoomStatus.findByName(request.status()); diff --git a/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomExistResponse.java b/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomExistResponse.java new file mode 100644 index 000000000..ccbb5968f --- /dev/null +++ b/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomExistResponse.java @@ -0,0 +1,7 @@ +package site.coduo.pairroom.service.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "페어룸 존재 여부 확인 응답 바디") +public record PairRoomExistResponse(@Schema(description = "페어룸 존재 확인") boolean exists) { +} diff --git a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java index b68cd09bb..eabe684f4 100644 --- a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java @@ -1,19 +1,20 @@ package site.coduo.acceptance; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; -import org.springframework.transaction.annotation.Transactional; import io.restassured.RestAssured; import io.restassured.http.ContentType; import site.coduo.pairroom.domain.PairRoomStatus; import site.coduo.pairroom.service.dto.PairRoomCreateRequest; import site.coduo.pairroom.service.dto.PairRoomCreateResponse; +import site.coduo.pairroom.service.dto.PairRoomExistResponse; -@Transactional class PairRoomAcceptanceTest extends AcceptanceFixture { static PairRoomCreateResponse createPairRoom(final PairRoomCreateRequest request) { @@ -98,4 +99,56 @@ void update_driver_navigator() { .then() .statusCode(204); } + + @Test + @DisplayName("페어룸이 존재하면 true를 반환한다.") + void exist_pair_room_true() { + //given + final PairRoomCreateResponse accessCode = + createPairRoom(new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "IN_PROGRESS")); + + // when & then + final PairRoomExistResponse response = RestAssured + .given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON_VALUE) + .log() + .all() + + .when() + .queryParam("access-code", accessCode.accessCode()) + .get("/api/pair-room/exist") + + .then() + .statusCode(200) + .extract() + .as(PairRoomExistResponse.class); + + assertThat(response.exists()).isTrue(); + } + + @Test + @DisplayName("페어룸이 존재하면 false를 반환한다.") + void exist_pair_room_false() { + //given + + // when & then + final PairRoomExistResponse response = RestAssured + .given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON_VALUE) + .log() + .all() + + .when() + .queryParam("access-code", "babyroom") + .get("/api/pair-room/exist") + + .then() + .statusCode(200) + .extract() + .as(PairRoomExistResponse.class); + + assertThat(response.exists()).isFalse(); + } } diff --git a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java index 6bfa85d9e..b02c50705 100644 --- a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java +++ b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.junit.jupiter.api.Assertions.assertAll; import java.util.List; import java.util.Random; @@ -201,4 +202,24 @@ void get_pair_room_and_timer() { .contains(pairRoomEntity.getNavigator(), pairRoomEntity.getDriver(), pairRoomEntity.getStatus().toString(), timer.getDuration(), timer.getRemainingTime()); } + + @Test + @DisplayName("페어룸이 존재하는지 확인한다.") + void exists_pair_room() { + //given + final AccessCode accessCode = new AccessCode("123456"); + final PairRoomEntity pairRoomEntity = PairRoomEntity.from( + new PairRoom(PairRoomStatus.IN_PROGRESS, + new Pair(new PairName("레디"), new PairName("레모네")), + accessCode + )); + pairRoomRepository.save(pairRoomEntity); + + //when & then + assertAll( + () -> assertThat(pairRoomService.existsByAccessCode("not-exist")).isFalse(), + () -> assertThat(pairRoomService.existsByAccessCode(accessCode.getValue())).isTrue() + + ); + } } From ca5658cd91805a5c5b92341fe1b514ff029dffa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Tue, 8 Oct 2024 13:23:08 +0900 Subject: [PATCH 52/74] =?UTF-8?q?feat:=20github=20OAuth=20=EA=B0=84?= =?UTF-8?q?=EC=86=8C=ED=99=94=20(#731)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/GithubOAuthController.java | 12 ++++-------- .../controller/docs/GithubOAuthControllerDocs.java | 6 ++---- .../service/dto/oauth/GithubOAuthEndpoint.java | 7 ------- .../site/coduo/acceptance/GithubAcceptanceTest.java | 13 ++++--------- 4 files changed, 10 insertions(+), 28 deletions(-) delete mode 100644 backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java diff --git a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java index 8319d7ce4..6166e02b6 100644 --- a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java @@ -1,8 +1,6 @@ package site.coduo.member.controller; -import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND; -import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME; import static site.coduo.member.controller.AuthController.PRODUCT_DOMAIN; import java.net.URI; @@ -26,7 +24,6 @@ import site.coduo.member.service.dto.oauth.GithubAuthQuery; import site.coduo.member.service.dto.oauth.GithubAuthUri; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; -import site.coduo.member.service.dto.oauth.GithubOAuthEndpoint; @Slf4j @RequiredArgsConstructor @@ -39,14 +36,13 @@ public class GithubOAuthController implements GithubOAuthControllerDocs { private String frontUrl; @GetMapping("/sign-in/oauth/github") - public ResponseEntity getGithubAuthCode(final HttpSession session) { + public ResponseEntity getGithubAuthCode(final HttpSession session) { final GithubAuthQuery query = githubOAuthService.createAuthorizationContent(); final GithubAuthUri githubAuthUri = new GithubAuthUri(query); - session.setAttribute(STATE_SESSION_NAME, query.state()); - session.setMaxInactiveInterval(STATE_SESSION_EXPIRE_IN_SECOND); - return ResponseEntity.ok() - .body(new GithubOAuthEndpoint(githubAuthUri.toPlainText())); + return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT) + .location(URI.create(githubAuthUri.toPlainText())) + .build(); } @GetMapping("/github/callback") diff --git a/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java b/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java index b3d1a69a3..da839b79a 100644 --- a/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java +++ b/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java @@ -14,21 +14,19 @@ import io.swagger.v3.oas.annotations.tags.Tag; import site.coduo.common.controller.response.ApiErrorResponse; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; -import site.coduo.member.service.dto.oauth.GithubOAuthEndpoint; @Tag(name = "깃허브 OAuth API") public interface GithubOAuthControllerDocs { @Operation(summary = "깃허브 인가엔드 포인트 URI 호출", responses = { - @ApiResponse(responseCode = "200", description = "깃허브 측 인가 엔드포인트 및 관련 Query 담은 URI", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = GithubOAuthEndpoint.class))), + @ApiResponse(responseCode = "307", description = "깃허브 측 인가 엔드 포인트로 redirect"), @ApiResponse(responseCode = "401", description = "인증 실패", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ApiErrorResponse.class))) } ) - ResponseEntity getGithubAuthCode(@Parameter(hidden = true) HttpSession session); + ResponseEntity getGithubAuthCode(@Parameter(hidden = true) HttpSession session); @ApiResponse(responseCode = "200") @ApiResponse(responseCode = "401", description = "인증 실패", diff --git a/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java b/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java deleted file mode 100644 index 39617b3a6..000000000 --- a/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java +++ /dev/null @@ -1,7 +0,0 @@ -package site.coduo.member.service.dto.oauth; - -import io.swagger.v3.oas.annotations.media.Schema; - -public record GithubOAuthEndpoint( - @Schema(description = "Github 인가 드포인트", example = "https://www.github.com/login/oauth/authorize?client_id=test&state=random%20number&redirect_uri=http://test.test") String endpoint) { -} diff --git a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java index 350afd1eb..2757f7a8b 100644 --- a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java @@ -1,8 +1,5 @@ package site.coduo.acceptance; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; - import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; import java.util.Map; @@ -10,7 +7,6 @@ import org.apache.http.HttpStatus; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpHeaders; import io.restassured.RestAssured; import site.coduo.fake.FakeGithubApiClient; @@ -51,9 +47,9 @@ void request_to_github_authorization_end_point() { .then().log().all() .assertThat() - .statusCode(HttpStatus.SC_OK) - .body("endpoint", - is("https://www.github.com/login/oauth/authorize?client_id=test&state=randomNumber&redirect_uri=http://test.test")); + .statusCode(307) + .header("Location", + "https://www.github.com/login/oauth/authorize?client_id=test&state=randomNumber&redirect_uri=http://test.test"); } @Test @@ -68,8 +64,7 @@ void call_github_authorize_endpoint() { .get("/api/sign-in/oauth/github") .then().log().all() - .statusCode(HttpStatus.SC_OK) - .header(HttpHeaders.SET_COOKIE, containsString("JSESSIONID")); + .statusCode(307); } @Test From 7cf9850279e02dd36438fab02b2a0ac1ae200726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Tue, 8 Oct 2024 13:26:15 +0900 Subject: [PATCH 53/74] =?UTF-8?q?feat:=20github=20OAuth=20=EA=B0=84?= =?UTF-8?q?=EC=86=8C=ED=99=94=20(#731)=20(#732)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/GithubOAuthController.java | 12 ++++-------- .../controller/docs/GithubOAuthControllerDocs.java | 6 ++---- .../service/dto/oauth/GithubOAuthEndpoint.java | 7 ------- .../site/coduo/acceptance/GithubAcceptanceTest.java | 13 ++++--------- 4 files changed, 10 insertions(+), 28 deletions(-) delete mode 100644 backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java diff --git a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java index 8319d7ce4..6166e02b6 100644 --- a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java @@ -1,8 +1,6 @@ package site.coduo.member.controller; -import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND; -import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME; import static site.coduo.member.controller.AuthController.PRODUCT_DOMAIN; import java.net.URI; @@ -26,7 +24,6 @@ import site.coduo.member.service.dto.oauth.GithubAuthQuery; import site.coduo.member.service.dto.oauth.GithubAuthUri; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; -import site.coduo.member.service.dto.oauth.GithubOAuthEndpoint; @Slf4j @RequiredArgsConstructor @@ -39,14 +36,13 @@ public class GithubOAuthController implements GithubOAuthControllerDocs { private String frontUrl; @GetMapping("/sign-in/oauth/github") - public ResponseEntity getGithubAuthCode(final HttpSession session) { + public ResponseEntity getGithubAuthCode(final HttpSession session) { final GithubAuthQuery query = githubOAuthService.createAuthorizationContent(); final GithubAuthUri githubAuthUri = new GithubAuthUri(query); - session.setAttribute(STATE_SESSION_NAME, query.state()); - session.setMaxInactiveInterval(STATE_SESSION_EXPIRE_IN_SECOND); - return ResponseEntity.ok() - .body(new GithubOAuthEndpoint(githubAuthUri.toPlainText())); + return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT) + .location(URI.create(githubAuthUri.toPlainText())) + .build(); } @GetMapping("/github/callback") diff --git a/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java b/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java index b3d1a69a3..da839b79a 100644 --- a/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java +++ b/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java @@ -14,21 +14,19 @@ import io.swagger.v3.oas.annotations.tags.Tag; import site.coduo.common.controller.response.ApiErrorResponse; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; -import site.coduo.member.service.dto.oauth.GithubOAuthEndpoint; @Tag(name = "깃허브 OAuth API") public interface GithubOAuthControllerDocs { @Operation(summary = "깃허브 인가엔드 포인트 URI 호출", responses = { - @ApiResponse(responseCode = "200", description = "깃허브 측 인가 엔드포인트 및 관련 Query 담은 URI", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = GithubOAuthEndpoint.class))), + @ApiResponse(responseCode = "307", description = "깃허브 측 인가 엔드 포인트로 redirect"), @ApiResponse(responseCode = "401", description = "인증 실패", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ApiErrorResponse.class))) } ) - ResponseEntity getGithubAuthCode(@Parameter(hidden = true) HttpSession session); + ResponseEntity getGithubAuthCode(@Parameter(hidden = true) HttpSession session); @ApiResponse(responseCode = "200") @ApiResponse(responseCode = "401", description = "인증 실패", diff --git a/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java b/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java deleted file mode 100644 index 39617b3a6..000000000 --- a/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java +++ /dev/null @@ -1,7 +0,0 @@ -package site.coduo.member.service.dto.oauth; - -import io.swagger.v3.oas.annotations.media.Schema; - -public record GithubOAuthEndpoint( - @Schema(description = "Github 인가 드포인트", example = "https://www.github.com/login/oauth/authorize?client_id=test&state=random%20number&redirect_uri=http://test.test") String endpoint) { -} diff --git a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java index 350afd1eb..2757f7a8b 100644 --- a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java @@ -1,8 +1,5 @@ package site.coduo.acceptance; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; - import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; import java.util.Map; @@ -10,7 +7,6 @@ import org.apache.http.HttpStatus; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpHeaders; import io.restassured.RestAssured; import site.coduo.fake.FakeGithubApiClient; @@ -51,9 +47,9 @@ void request_to_github_authorization_end_point() { .then().log().all() .assertThat() - .statusCode(HttpStatus.SC_OK) - .body("endpoint", - is("https://www.github.com/login/oauth/authorize?client_id=test&state=randomNumber&redirect_uri=http://test.test")); + .statusCode(307) + .header("Location", + "https://www.github.com/login/oauth/authorize?client_id=test&state=randomNumber&redirect_uri=http://test.test"); } @Test @@ -68,8 +64,7 @@ void call_github_authorize_endpoint() { .get("/api/sign-in/oauth/github") .then().log().all() - .statusCode(HttpStatus.SC_OK) - .header(HttpHeaders.SET_COOKIE, containsString("JSESSIONID")); + .statusCode(307); } @Test From 2d4fbeec9222e5fba458552021560541f4727fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Tue, 8 Oct 2024 13:41:51 +0900 Subject: [PATCH 54/74] =?UTF-8?q?[BE]=20CORS=20=EC=B6=94=EA=B0=80=20(#733)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: github OAuth 간소화 * feat: github OAuth 간소화 --- .../main/java/site/coduo/common/config/web/WebMvcConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java b/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java index 871f70145..291ff1e6a 100644 --- a/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java @@ -23,7 +23,7 @@ public void addCorsMappings(final CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD") .allowedOrigins("http://localhost:3000", "https://coduo.site", "https://test.coduo.site", - "https://api-test.coduo.site", "https://api.coduo.site") + "https://api-test.coduo.site", "https://api.coduo.site", "https://www.github.com") .allowCredentials(true); } } From 747a3a44a348829a873b8055d17fdf28195db8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Tue, 8 Oct 2024 13:44:47 +0900 Subject: [PATCH 55/74] =?UTF-8?q?[BE]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EB=B0=B0=ED=8F=AC=20=20(#734)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: github OAuth 간소화 (#731) * [BE] CORS 추가 (#733) * feat: github OAuth 간소화 * feat: github OAuth 간소화 --- .../main/java/site/coduo/common/config/web/WebMvcConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java b/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java index 871f70145..291ff1e6a 100644 --- a/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java @@ -23,7 +23,7 @@ public void addCorsMappings(final CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD") .allowedOrigins("http://localhost:3000", "https://coduo.site", "https://test.coduo.site", - "https://api-test.coduo.site", "https://api.coduo.site") + "https://api-test.coduo.site", "https://api.coduo.site", "https://www.github.com") .allowCredentials(true); } } From 8ccde6d3779db2bf59dcb99806280f17992941aa Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Tue, 8 Oct 2024 14:50:58 +0900 Subject: [PATCH 56/74] =?UTF-8?q?refactor:=20=ED=8E=98=EC=96=B4=EB=A3=B8?= =?UTF-8?q?=20=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=20API=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coduo/pairroom/controller/PairRoomController.java | 4 ++-- .../site/coduo/acceptance/PairRoomAcceptanceTest.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java index 093deb972..825e5db79 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java @@ -85,8 +85,8 @@ public ResponseEntity> getPairRooms( .body(pairRooms); } - @GetMapping("/pair-room/exist") - public ResponseEntity pairRoomExist(@RequestParam("access-code") final String accessCode) { + @GetMapping("/pair-room/exists") + public ResponseEntity pairRoomExist(@RequestParam("access_code") final String accessCode) { final PairRoomExistResponse response = new PairRoomExistResponse( pairRoomService.existsByAccessCode(accessCode)); diff --git a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java index eabe684f4..7dab4e66c 100644 --- a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java @@ -116,8 +116,8 @@ void exist_pair_room_true() { .all() .when() - .queryParam("access-code", accessCode.accessCode()) - .get("/api/pair-room/exist") + .queryParam("access_code", accessCode.accessCode()) + .get("/api/pair-room/exists") .then() .statusCode(200) @@ -141,8 +141,8 @@ void exist_pair_room_false() { .all() .when() - .queryParam("access-code", "babyroom") - .get("/api/pair-room/exist") + .queryParam("access_code", "babyroom") + .get("/api/pair-room/exists") .then() .statusCode(200) From 94e2d1156809b40eb333d37713c57ba9d39756cc Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Tue, 8 Oct 2024 14:54:00 +0900 Subject: [PATCH 57/74] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/site/coduo/pairroom/controller/PairRoomController.java | 2 +- .../java/site/coduo/pairroom/controller/docs/PairRoomDocs.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java index 825e5db79..c27bc6200 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java @@ -86,7 +86,7 @@ public ResponseEntity> getPairRooms( } @GetMapping("/pair-room/exists") - public ResponseEntity pairRoomExist(@RequestParam("access_code") final String accessCode) { + public ResponseEntity pairRoomExists(@RequestParam("access_code") final String accessCode) { final PairRoomExistResponse response = new PairRoomExistResponse( pairRoomService.existsByAccessCode(accessCode)); diff --git a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java index 91a46218b..f2727b273 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java @@ -73,5 +73,5 @@ ResponseEntity> getPairRooms( @Operation(summary = "액세스 코드로 페어룸이 존재하는지 조회한다.") @ApiResponse(responseCode = "200", description = "페어룸 존재 여부", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = PairRoomExistResponse.class))) - ResponseEntity pairRoomExist(String accessCode); + ResponseEntity pairRoomExists(String accessCode); } From 6433d7432d8cd52207f04b5acb7a8ea62b5a5b09 Mon Sep 17 00:00:00 2001 From: anttiey Date: Tue, 8 Oct 2024 15:17:53 +0900 Subject: [PATCH 58/74] =?UTF-8?q?:sparkles:=20=ED=8E=98=EC=96=B4=EB=A3=B8?= =?UTF-8?q?=20=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/apis/pairRoom.ts | 9 +++++++++ .../PairRoomEntryModal/PairRoomEntryModal.tsx | 20 ++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/frontend/src/apis/pairRoom.ts b/frontend/src/apis/pairRoom.ts index 12f8a8d20..2a917c660 100644 --- a/frontend/src/apis/pairRoom.ts +++ b/frontend/src/apis/pairRoom.ts @@ -22,6 +22,15 @@ export const getPairRoom = async (accessCode: string): Promise => { + const response = await fetcher.get({ + url: `${API_URL}/pair-room/exists?access_code=${accessCode}`, + errorMessage: ERROR_MESSAGES.GET_PAIR_ROOM, + }); + + return await response.json(); +}; + interface AddPairRoomRequest { driver: string; navigator: string; diff --git a/frontend/src/components/Main/PairRoomEntryModal/PairRoomEntryModal.tsx b/frontend/src/components/Main/PairRoomEntryModal/PairRoomEntryModal.tsx index 2207b2176..6da864096 100644 --- a/frontend/src/components/Main/PairRoomEntryModal/PairRoomEntryModal.tsx +++ b/frontend/src/components/Main/PairRoomEntryModal/PairRoomEntryModal.tsx @@ -6,9 +6,9 @@ import { Modal } from '@/components/common/Modal'; import useToastStore from '@/stores/toastStore'; -import useInput from '@/hooks/common/useInput'; +import { getPairRoomExists } from '@/apis/pairRoom'; -import useGetPairRoom from '@/queries/PairRoom/useGetPairRoom'; +import useInput from '@/hooks/common/useInput'; import { BUTTON_TEXT } from '@/constants/button'; @@ -19,23 +19,19 @@ interface PairRoomEntryModal { const PairRoomEntryModal = ({ isOpen, closeModal }: PairRoomEntryModal) => { const navigate = useNavigate(); - const { addToast } = useToastStore(); + const { addToast } = useToastStore(); const { value, status, message, handleChange } = useInput(); - const { refetch } = useGetPairRoom(value); - const enterPairRoom = async () => { - const { error, isFetching, isSuccess } = await refetch(); + const { exists } = await getPairRoomExists(value); - if (error) { - addToast({ status: 'ERROR', message: '해당 코드와 일치하는 방이 없습니다 🥹' }); + if (!exists) { + addToast({ status: 'ERROR', message: '해당 코드와 일치하는 방이 없습니다.' }); return; } - if (!isFetching && isSuccess) { - navigate(`/room/${value}`); - } + navigate(`/room/${value}`); }; return ( @@ -55,7 +51,7 @@ const PairRoomEntryModal = ({ isOpen, closeModal }: PairRoomEntryModal) => { - From 2e38d050e426888312f4f0b6cc12ed9d15e4d1ee Mon Sep 17 00:00:00 2001 From: anttiey Date: Tue, 8 Oct 2024 15:21:25 +0900 Subject: [PATCH 59/74] =?UTF-8?q?:recycle:=20=ED=88=AC=EB=91=90=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B3=B5=EB=B0=B1=EB=A7=8C=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EC=8B=9C=20=ED=99=9C=EC=84=B1=ED=99=94?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx b/frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx index a05fa1166..f571d6567 100644 --- a/frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx +++ b/frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx @@ -46,7 +46,7 @@ const TodoListCard = ({ isOpen, toggleIsOpen }: TodoListCardProps) => { maxLength={100} placeholder="할 일의 내용을 입력해 주세요." /> - From ba925b65a95ac707b6118c0c11342680d8da1fdb Mon Sep 17 00:00:00 2001 From: fram1998 Date: Tue, 8 Oct 2024 15:28:33 +0900 Subject: [PATCH 60/74] =?UTF-8?q?Revert=20"[BE]=20CORS=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#733)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2d4fbeec9222e5fba458552021560541f4727fbc. --- .../main/java/site/coduo/common/config/web/WebMvcConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java b/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java index 291ff1e6a..871f70145 100644 --- a/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java @@ -23,7 +23,7 @@ public void addCorsMappings(final CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD") .allowedOrigins("http://localhost:3000", "https://coduo.site", "https://test.coduo.site", - "https://api-test.coduo.site", "https://api.coduo.site", "https://www.github.com") + "https://api-test.coduo.site", "https://api.coduo.site") .allowCredentials(true); } } From 05db3e2eaadf4e7cd614d3e1bcb39598a144c513 Mon Sep 17 00:00:00 2001 From: fram1998 Date: Tue, 8 Oct 2024 15:28:51 +0900 Subject: [PATCH 61/74] =?UTF-8?q?Revert=20"feat:=20github=20OAuth=20?= =?UTF-8?q?=EA=B0=84=EC=86=8C=ED=99=94=20(#731)=20(#732)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 7cf9850279e02dd36438fab02b2a0ac1ae200726. --- .../member/controller/GithubOAuthController.java | 12 ++++++++---- .../controller/docs/GithubOAuthControllerDocs.java | 6 ++++-- .../service/dto/oauth/GithubOAuthEndpoint.java | 7 +++++++ .../site/coduo/acceptance/GithubAcceptanceTest.java | 13 +++++++++---- 4 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java diff --git a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java index 6166e02b6..8319d7ce4 100644 --- a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java @@ -1,6 +1,8 @@ package site.coduo.member.controller; +import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND; +import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME; import static site.coduo.member.controller.AuthController.PRODUCT_DOMAIN; import java.net.URI; @@ -24,6 +26,7 @@ import site.coduo.member.service.dto.oauth.GithubAuthQuery; import site.coduo.member.service.dto.oauth.GithubAuthUri; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; +import site.coduo.member.service.dto.oauth.GithubOAuthEndpoint; @Slf4j @RequiredArgsConstructor @@ -36,13 +39,14 @@ public class GithubOAuthController implements GithubOAuthControllerDocs { private String frontUrl; @GetMapping("/sign-in/oauth/github") - public ResponseEntity getGithubAuthCode(final HttpSession session) { + public ResponseEntity getGithubAuthCode(final HttpSession session) { final GithubAuthQuery query = githubOAuthService.createAuthorizationContent(); final GithubAuthUri githubAuthUri = new GithubAuthUri(query); - return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT) - .location(URI.create(githubAuthUri.toPlainText())) - .build(); + session.setAttribute(STATE_SESSION_NAME, query.state()); + session.setMaxInactiveInterval(STATE_SESSION_EXPIRE_IN_SECOND); + return ResponseEntity.ok() + .body(new GithubOAuthEndpoint(githubAuthUri.toPlainText())); } @GetMapping("/github/callback") diff --git a/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java b/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java index da839b79a..b3d1a69a3 100644 --- a/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java +++ b/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java @@ -14,19 +14,21 @@ import io.swagger.v3.oas.annotations.tags.Tag; import site.coduo.common.controller.response.ApiErrorResponse; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; +import site.coduo.member.service.dto.oauth.GithubOAuthEndpoint; @Tag(name = "깃허브 OAuth API") public interface GithubOAuthControllerDocs { @Operation(summary = "깃허브 인가엔드 포인트 URI 호출", responses = { - @ApiResponse(responseCode = "307", description = "깃허브 측 인가 엔드 포인트로 redirect"), + @ApiResponse(responseCode = "200", description = "깃허브 측 인가 엔드포인트 및 관련 Query 담은 URI", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = GithubOAuthEndpoint.class))), @ApiResponse(responseCode = "401", description = "인증 실패", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ApiErrorResponse.class))) } ) - ResponseEntity getGithubAuthCode(@Parameter(hidden = true) HttpSession session); + ResponseEntity getGithubAuthCode(@Parameter(hidden = true) HttpSession session); @ApiResponse(responseCode = "200") @ApiResponse(responseCode = "401", description = "인증 실패", diff --git a/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java b/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java new file mode 100644 index 000000000..39617b3a6 --- /dev/null +++ b/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java @@ -0,0 +1,7 @@ +package site.coduo.member.service.dto.oauth; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record GithubOAuthEndpoint( + @Schema(description = "Github 인가 드포인트", example = "https://www.github.com/login/oauth/authorize?client_id=test&state=random%20number&redirect_uri=http://test.test") String endpoint) { +} diff --git a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java index 2757f7a8b..350afd1eb 100644 --- a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java @@ -1,5 +1,8 @@ package site.coduo.acceptance; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; import java.util.Map; @@ -7,6 +10,7 @@ import org.apache.http.HttpStatus; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; import io.restassured.RestAssured; import site.coduo.fake.FakeGithubApiClient; @@ -47,9 +51,9 @@ void request_to_github_authorization_end_point() { .then().log().all() .assertThat() - .statusCode(307) - .header("Location", - "https://www.github.com/login/oauth/authorize?client_id=test&state=randomNumber&redirect_uri=http://test.test"); + .statusCode(HttpStatus.SC_OK) + .body("endpoint", + is("https://www.github.com/login/oauth/authorize?client_id=test&state=randomNumber&redirect_uri=http://test.test")); } @Test @@ -64,7 +68,8 @@ void call_github_authorize_endpoint() { .get("/api/sign-in/oauth/github") .then().log().all() - .statusCode(307); + .statusCode(HttpStatus.SC_OK) + .header(HttpHeaders.SET_COOKIE, containsString("JSESSIONID")); } @Test From 52081aca810617ac64d9c39dae7c0766dfc6ad15 Mon Sep 17 00:00:00 2001 From: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Date: Tue, 8 Oct 2024 15:35:57 +0900 Subject: [PATCH 62/74] =?UTF-8?q?[BE]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EB=B0=B0=ED=8F=AC=20(#736)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 링크 삭제 시 액세스코드 검증 추가 * test: 테스트용 fakeServer 구현 * test: fakeServer 테스트 추가 * test: OpenGraphServiceTest 테스트 개선 * test: 레퍼런스링크와 오픈그래프 테스트 개선 * refactor: 페어룸으로 레퍼런스링크 찾는 메서드 * feat: github OAuth 간소화 (#731) * [BE] CORS 추가 (#733) * feat: github OAuth 간소화 * feat: github OAuth 간소화 * refactor: 페어룸 존재 여부 확인 API 변경 * refactor: 메서드 이름 변경 * Revert "[BE] CORS 추가 (#733)" This reverts commit 2d4fbeec9222e5fba458552021560541f4727fbc. * Revert "feat: github OAuth 간소화 (#731) (#732)" This reverts commit 7cf9850279e02dd36438fab02b2a0ac1ae200726. --------- Co-authored-by: yechop Co-authored-by: 이예찬 <148426765+yechop@users.noreply.github.com> Co-authored-by: 김민종 --- .../coduo/common/config/web/WebMvcConfig.java | 2 +- .../controller/GithubOAuthController.java | 12 +- .../docs/GithubOAuthControllerDocs.java | 6 +- .../dto/oauth/GithubOAuthEndpoint.java | 7 + .../controller/PairRoomController.java | 4 +- .../controller/docs/PairRoomDocs.java | 2 +- .../controller/ReferenceLinkController.java | 2 +- .../repository/OpenGraphRepository.java | 2 +- .../repository/ReferenceLinkRepository.java | 6 + .../service/OpenGraphService.java | 4 +- .../service/ReferenceLinkService.java | 9 +- .../acceptance/GithubAcceptanceTest.java | 13 +- .../acceptance/PairRoomAcceptanceTest.java | 8 +- .../coduo/referencelink/fake/FakeServer.java | 55 ++++++++ .../referencelink/fake/FakeServerTest.java | 21 +++ .../service/CategoryServiceTest.java | 7 +- .../service/OpenGraphServiceTest.java | 128 +++++++++++++----- .../service/ReferenceLinkServiceTest.java | 107 +++++++++++---- 18 files changed, 310 insertions(+), 85 deletions(-) create mode 100644 backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java create mode 100644 backend/src/test/java/site/coduo/referencelink/fake/FakeServer.java create mode 100644 backend/src/test/java/site/coduo/referencelink/fake/FakeServerTest.java diff --git a/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java b/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java index 291ff1e6a..871f70145 100644 --- a/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java +++ b/backend/src/main/java/site/coduo/common/config/web/WebMvcConfig.java @@ -23,7 +23,7 @@ public void addCorsMappings(final CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD") .allowedOrigins("http://localhost:3000", "https://coduo.site", "https://test.coduo.site", - "https://api-test.coduo.site", "https://api.coduo.site", "https://www.github.com") + "https://api-test.coduo.site", "https://api.coduo.site") .allowCredentials(true); } } diff --git a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java index 6166e02b6..8319d7ce4 100644 --- a/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java +++ b/backend/src/main/java/site/coduo/member/controller/GithubOAuthController.java @@ -1,6 +1,8 @@ package site.coduo.member.controller; +import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND; +import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME; import static site.coduo.member.controller.AuthController.PRODUCT_DOMAIN; import java.net.URI; @@ -24,6 +26,7 @@ import site.coduo.member.service.dto.oauth.GithubAuthQuery; import site.coduo.member.service.dto.oauth.GithubAuthUri; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; +import site.coduo.member.service.dto.oauth.GithubOAuthEndpoint; @Slf4j @RequiredArgsConstructor @@ -36,13 +39,14 @@ public class GithubOAuthController implements GithubOAuthControllerDocs { private String frontUrl; @GetMapping("/sign-in/oauth/github") - public ResponseEntity getGithubAuthCode(final HttpSession session) { + public ResponseEntity getGithubAuthCode(final HttpSession session) { final GithubAuthQuery query = githubOAuthService.createAuthorizationContent(); final GithubAuthUri githubAuthUri = new GithubAuthUri(query); - return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT) - .location(URI.create(githubAuthUri.toPlainText())) - .build(); + session.setAttribute(STATE_SESSION_NAME, query.state()); + session.setMaxInactiveInterval(STATE_SESSION_EXPIRE_IN_SECOND); + return ResponseEntity.ok() + .body(new GithubOAuthEndpoint(githubAuthUri.toPlainText())); } @GetMapping("/github/callback") diff --git a/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java b/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java index da839b79a..b3d1a69a3 100644 --- a/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java +++ b/backend/src/main/java/site/coduo/member/controller/docs/GithubOAuthControllerDocs.java @@ -14,19 +14,21 @@ import io.swagger.v3.oas.annotations.tags.Tag; import site.coduo.common.controller.response.ApiErrorResponse; import site.coduo.member.service.dto.oauth.GithubCallbackQuery; +import site.coduo.member.service.dto.oauth.GithubOAuthEndpoint; @Tag(name = "깃허브 OAuth API") public interface GithubOAuthControllerDocs { @Operation(summary = "깃허브 인가엔드 포인트 URI 호출", responses = { - @ApiResponse(responseCode = "307", description = "깃허브 측 인가 엔드 포인트로 redirect"), + @ApiResponse(responseCode = "200", description = "깃허브 측 인가 엔드포인트 및 관련 Query 담은 URI", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = GithubOAuthEndpoint.class))), @ApiResponse(responseCode = "401", description = "인증 실패", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ApiErrorResponse.class))) } ) - ResponseEntity getGithubAuthCode(@Parameter(hidden = true) HttpSession session); + ResponseEntity getGithubAuthCode(@Parameter(hidden = true) HttpSession session); @ApiResponse(responseCode = "200") @ApiResponse(responseCode = "401", description = "인증 실패", diff --git a/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java b/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java new file mode 100644 index 000000000..39617b3a6 --- /dev/null +++ b/backend/src/main/java/site/coduo/member/service/dto/oauth/GithubOAuthEndpoint.java @@ -0,0 +1,7 @@ +package site.coduo.member.service.dto.oauth; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record GithubOAuthEndpoint( + @Schema(description = "Github 인가 드포인트", example = "https://www.github.com/login/oauth/authorize?client_id=test&state=random%20number&redirect_uri=http://test.test") String endpoint) { +} diff --git a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java index 093deb972..c27bc6200 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java @@ -85,8 +85,8 @@ public ResponseEntity> getPairRooms( .body(pairRooms); } - @GetMapping("/pair-room/exist") - public ResponseEntity pairRoomExist(@RequestParam("access-code") final String accessCode) { + @GetMapping("/pair-room/exists") + public ResponseEntity pairRoomExists(@RequestParam("access_code") final String accessCode) { final PairRoomExistResponse response = new PairRoomExistResponse( pairRoomService.existsByAccessCode(accessCode)); diff --git a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java index 91a46218b..f2727b273 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java @@ -73,5 +73,5 @@ ResponseEntity> getPairRooms( @Operation(summary = "액세스 코드로 페어룸이 존재하는지 조회한다.") @ApiResponse(responseCode = "200", description = "페어룸 존재 여부", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = PairRoomExistResponse.class))) - ResponseEntity pairRoomExist(String accessCode); + ResponseEntity pairRoomExists(String accessCode); } diff --git a/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java b/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java index 6145bf32e..40f6d31f5 100644 --- a/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java +++ b/backend/src/main/java/site/coduo/referencelink/controller/ReferenceLinkController.java @@ -65,7 +65,7 @@ public ResponseEntity deleteReferenceLink( @PathVariable("accessCode") final String accessCodeText, @PathVariable("id") final long id ) { - referenceLinkService.deleteReferenceLink(id); + referenceLinkService.deleteReferenceLink(accessCodeText, id); return ResponseEntity.noContent() .build(); diff --git a/backend/src/main/java/site/coduo/referencelink/repository/OpenGraphRepository.java b/backend/src/main/java/site/coduo/referencelink/repository/OpenGraphRepository.java index 883e6b69d..e1df1a33f 100644 --- a/backend/src/main/java/site/coduo/referencelink/repository/OpenGraphRepository.java +++ b/backend/src/main/java/site/coduo/referencelink/repository/OpenGraphRepository.java @@ -6,7 +6,7 @@ public interface OpenGraphRepository extends JpaRepository { - void deleteByReferenceLinkEntityId(Long referenceLinkEntityId); + void deleteByReferenceLinkEntity(ReferenceLinkEntity referenceLinkEntity); Optional findByReferenceLinkEntityId(Long id); } diff --git a/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java b/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java index a9bbde027..9721e5a7c 100644 --- a/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java +++ b/backend/src/main/java/site/coduo/referencelink/repository/ReferenceLinkRepository.java @@ -5,8 +5,14 @@ import org.springframework.data.jpa.repository.JpaRepository; import site.coduo.pairroom.repository.PairRoomEntity; +import site.coduo.referencelink.exception.ReferenceLinkException; public interface ReferenceLinkRepository extends JpaRepository { List findByPairRoomEntity(PairRoomEntity pairRoomEntity); + + default ReferenceLinkEntity fetchById(long id) { + return findById(id) + .orElseThrow(() -> new ReferenceLinkException("존재하지 않는 링크입니다.")); + } } diff --git a/backend/src/main/java/site/coduo/referencelink/service/OpenGraphService.java b/backend/src/main/java/site/coduo/referencelink/service/OpenGraphService.java index 7a233a405..5f360fc29 100644 --- a/backend/src/main/java/site/coduo/referencelink/service/OpenGraphService.java +++ b/backend/src/main/java/site/coduo/referencelink/service/OpenGraphService.java @@ -69,7 +69,7 @@ public OpenGraph findOpenGraph(final Long id) { return new OpenGraph(); } - public void deleteByReferenceLinkId(final long referenceLinkEntityId) { - openGraphRepository.deleteByReferenceLinkEntityId(referenceLinkEntityId); + public void deleteByReferenceLink(final ReferenceLinkEntity referenceLinkEntity) { + openGraphRepository.deleteByReferenceLinkEntity(referenceLinkEntity); } } diff --git a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java index e22fb208d..68eec979f 100644 --- a/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java +++ b/backend/src/main/java/site/coduo/referencelink/service/ReferenceLinkService.java @@ -117,8 +117,11 @@ private ReferenceLinkResponse makeReferenceLinkResponse(final ReferenceLinkEntit return new ReferenceLinkResponse(referenceLinkEntity, openGraph); } - public void deleteReferenceLink(final long id) { - openGraphService.deleteByReferenceLinkId(id); - referenceLinkRepository.deleteById(id); + public void deleteReferenceLink(final String accessCodeText, final long id) { + final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.fetchById(id); + if (referenceLinkEntity.isSameAccessCode(new AccessCode(accessCodeText))) { + openGraphService.deleteByReferenceLink(referenceLinkEntity); + referenceLinkRepository.delete(referenceLinkEntity); + } } } diff --git a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java index 2757f7a8b..350afd1eb 100644 --- a/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/GithubAcceptanceTest.java @@ -1,5 +1,8 @@ package site.coduo.acceptance; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME; import java.util.Map; @@ -7,6 +10,7 @@ import org.apache.http.HttpStatus; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; import io.restassured.RestAssured; import site.coduo.fake.FakeGithubApiClient; @@ -47,9 +51,9 @@ void request_to_github_authorization_end_point() { .then().log().all() .assertThat() - .statusCode(307) - .header("Location", - "https://www.github.com/login/oauth/authorize?client_id=test&state=randomNumber&redirect_uri=http://test.test"); + .statusCode(HttpStatus.SC_OK) + .body("endpoint", + is("https://www.github.com/login/oauth/authorize?client_id=test&state=randomNumber&redirect_uri=http://test.test")); } @Test @@ -64,7 +68,8 @@ void call_github_authorize_endpoint() { .get("/api/sign-in/oauth/github") .then().log().all() - .statusCode(307); + .statusCode(HttpStatus.SC_OK) + .header(HttpHeaders.SET_COOKIE, containsString("JSESSIONID")); } @Test diff --git a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java index eabe684f4..7dab4e66c 100644 --- a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java @@ -116,8 +116,8 @@ void exist_pair_room_true() { .all() .when() - .queryParam("access-code", accessCode.accessCode()) - .get("/api/pair-room/exist") + .queryParam("access_code", accessCode.accessCode()) + .get("/api/pair-room/exists") .then() .statusCode(200) @@ -141,8 +141,8 @@ void exist_pair_room_false() { .all() .when() - .queryParam("access-code", "babyroom") - .get("/api/pair-room/exist") + .queryParam("access_code", "babyroom") + .get("/api/pair-room/exists") .then() .statusCode(200) diff --git a/backend/src/test/java/site/coduo/referencelink/fake/FakeServer.java b/backend/src/test/java/site/coduo/referencelink/fake/FakeServer.java new file mode 100644 index 000000000..81718b7d3 --- /dev/null +++ b/backend/src/test/java/site/coduo/referencelink/fake/FakeServer.java @@ -0,0 +1,55 @@ +package site.coduo.referencelink.fake; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +import site.coduo.referencelink.exception.ReferenceLinkException; + +public class FakeServer { + + public static final String testUrl; + + static { + final String html = "" + + "헤드 타이틀" + + "" + + "" + + "" + + ""; + testUrl = "http://localhost:" + createAndStartFakeServer(html) + "/test"; + } + + public static int createAndStartFakeServer(final String html) { + HttpServer server; + try { + server = HttpServer.create(new InetSocketAddress(0), 0); + } catch (final IOException e) { + throw new ReferenceLinkException("테스트용 서버 생성에 실패했습니다."); + } + final int assignedPort = server.getAddress().getPort(); + + server.createContext("/test", createHandler(html)); + server.setExecutor(null); + server.start(); + return assignedPort; + } + + private static HttpHandler createHandler(final String html) { + return new HttpHandler() { + @Override + public void handle(final HttpExchange exchange) throws IOException { + exchange.getResponseHeaders().set("Content-Type", "text/html; charset=UTF-8"); + exchange.sendResponseHeaders(200, html.getBytes(StandardCharsets.UTF_8).length); + final OutputStream os = exchange.getResponseBody(); + os.write(html.getBytes(StandardCharsets.UTF_8)); + os.close(); + } + }; + } +} diff --git a/backend/src/test/java/site/coduo/referencelink/fake/FakeServerTest.java b/backend/src/test/java/site/coduo/referencelink/fake/FakeServerTest.java new file mode 100644 index 000000000..ca52af9c8 --- /dev/null +++ b/backend/src/test/java/site/coduo/referencelink/fake/FakeServerTest.java @@ -0,0 +1,21 @@ +package site.coduo.referencelink.fake; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class FakeServerTest { + + @DisplayName("페이크 서버 연결에 성공한다.") + @Test + void connect_success() throws IOException { + final URL url = new URL(FakeServer.testUrl); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + assertThat(connection.getResponseCode()).isEqualTo(200); + } +} diff --git a/backend/src/test/java/site/coduo/referencelink/service/CategoryServiceTest.java b/backend/src/test/java/site/coduo/referencelink/service/CategoryServiceTest.java index a0bb90c18..770e604e6 100644 --- a/backend/src/test/java/site/coduo/referencelink/service/CategoryServiceTest.java +++ b/backend/src/test/java/site/coduo/referencelink/service/CategoryServiceTest.java @@ -23,6 +23,7 @@ import site.coduo.referencelink.domain.Category; import site.coduo.referencelink.domain.ReferenceLink; import site.coduo.referencelink.exception.InvalidCategoryException; +import site.coduo.referencelink.fake.FakeServer; import site.coduo.referencelink.repository.CategoryEntity; import site.coduo.referencelink.repository.CategoryRepository; import site.coduo.referencelink.repository.ReferenceLinkEntity; @@ -70,7 +71,7 @@ void save_category() { ACCESS_CODE.getValue()); assertThat(categories.stream().anyMatch( category -> category.id().equals(createdCategory.id()) && - category.value().equals(createdCategory.value()))) + category.value().equals(createdCategory.value()))) .isTrue(); } @@ -93,7 +94,7 @@ void update_category() { ACCESS_CODE.getValue()); assertThat(categories.stream().anyMatch( category -> category.id().equals(createdCategory.id()) && - category.value().equals(updatedCategory.updatedCategoryName()))) + category.value().equals(updatedCategory.updatedCategoryName()))) .isTrue(); } @@ -161,7 +162,7 @@ void remove_category_and_update_reference_category_value() throws MalformedURLEx final PairRoomEntity entity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); final CategoryEntity savedCategory = categoryRepository.save(new CategoryEntity(entity, category)); - final ReferenceLink referenceLink = new ReferenceLink(new URL("https://google.com"), ACCESS_CODE); + final ReferenceLink referenceLink = new ReferenceLink(new URL(FakeServer.testUrl), ACCESS_CODE); final ReferenceLinkEntity beforeDeleteCategory = referenceLinkRepository.save( new ReferenceLinkEntity(referenceLink, savedCategory, entity)); diff --git a/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java b/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java index ed9b513dc..e649176cf 100644 --- a/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java +++ b/backend/src/test/java/site/coduo/referencelink/service/OpenGraphServiceTest.java @@ -5,10 +5,12 @@ import static site.coduo.fixture.PairRoomFixture.INK_REDDDY_ROOM; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +22,7 @@ import site.coduo.referencelink.domain.Category; import site.coduo.referencelink.domain.OpenGraph; import site.coduo.referencelink.domain.ReferenceLink; +import site.coduo.referencelink.fake.FakeServer; import site.coduo.referencelink.repository.CategoryEntity; import site.coduo.referencelink.repository.CategoryRepository; import site.coduo.referencelink.repository.OpenGraphRepository; @@ -47,6 +50,16 @@ class OpenGraphServiceTest extends CascadeCleaner { @Autowired private CategoryRepository categoryRepository; + private PairRoomEntity pairRoomEntity; + private CategoryEntity category; + + @BeforeEach + void setUp() { + pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); + category = categoryRepository.save( + new CategoryEntity(pairRoomEntity, new Category("스프링"))); + } + @AfterEach void tearDown() { deleteAllPairRoomCascade(); @@ -54,26 +67,22 @@ void tearDown() { @Test @DisplayName("오픈그래프를 생성 후 저장한다.") - void create_open_graph() throws MalformedURLException { + void create_open_graph_exactly() throws IOException { //given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - - final CategoryEntity category = categoryRepository.save( - new CategoryEntity(pairRoomEntity, new Category("스프링"))); - final URL url = new URL("https://www.naver.com"); - final ReferenceLinkEntity referenceLink = new ReferenceLinkEntity( - new ReferenceLink(url, new AccessCode(pairRoomEntity.getAccessCode())), - category, - pairRoomEntity - ); + final URL url = new URL(FakeServer.testUrl); + final ReferenceLinkEntity referenceLink = generateReferenceLinkEntity(url); final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.save(referenceLink); // when - openGraphService.createOpenGraph(referenceLinkEntity, url); + final OpenGraph openGraph = openGraphService.createOpenGraph(referenceLinkEntity, url); - // then - assertThat(openGraphRepository.findAll()) - .hasSize(1); + //then + assertAll( + () -> assertThat(openGraphRepository.findAll()).hasSize(1), + () -> assertThat(openGraph) + .extracting("headTitle", "openGraphTitle", "description", "image") + .contains("헤드 타이틀", "오픈그래프 타이틀", "오픈그래프 설명", "오픈그래프 이미지") + ); } @DisplayName("일치하는 오픈그래프가 없으면 기본 값을 넣은 오픈그래프를 반환한다.") @@ -83,34 +92,89 @@ void return_null_when_cannot_find_open_graph() { final OpenGraph openGraph = openGraphService.findOpenGraph(1L); // then - assertAll( - () -> assertThat(openGraph.getHeadTitle()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE), - () -> assertThat(openGraph.getOpenGraphTitle()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE), - () -> assertThat(openGraph.getDescription()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE), - () -> assertThat(openGraph.getImage()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE) - ); + assertThat(openGraph) + .extracting("headTitle", "openGraphTitle", "description", "image") + .contains(DEFAULT_OPEN_GRAPH_VALUE, DEFAULT_OPEN_GRAPH_VALUE, DEFAULT_OPEN_GRAPH_VALUE, + DEFAULT_OPEN_GRAPH_VALUE); } - @DisplayName("레퍼런스링크 id로 오픈그래프를 삭제한다.") + @DisplayName("레퍼런스링크로 오픈그래프를 삭제한다.") @Test void delete_open_graph_by_reference_link_id() throws MalformedURLException { // given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final CategoryEntity category = categoryRepository.save( - new CategoryEntity(pairRoomEntity, new Category("스프링"))); - final URL url = new URL("https://www.naver.com"); - final ReferenceLinkEntity referenceLink = new ReferenceLinkEntity( - new ReferenceLink(url, new AccessCode(pairRoomEntity.getAccessCode())), - category, - pairRoomEntity - ); + final URL url = new URL(FakeServer.testUrl); + final ReferenceLinkEntity referenceLink = generateReferenceLinkEntity(url); final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.save(referenceLink); openGraphService.createOpenGraph(referenceLinkEntity, url); // when - openGraphService.deleteByReferenceLinkId(referenceLinkEntity.getId()); + openGraphService.deleteByReferenceLink(referenceLinkEntity); // then assertThat(openGraphRepository.findAll()).isEmpty(); } + + @DisplayName("링크의 도큐먼트를 가져오지 못했을때 헤드타이틀에 도메인을 넣어 생성 후 저장한다.") + @Test + void create_openGraph_when_cannot_get_document() throws IOException { + //given + final int assignedPort = FakeServer.createAndStartFakeServer(null); + final URL url = new URL("http://localhost:" + assignedPort + "/test"); + final ReferenceLinkEntity referenceLink = generateReferenceLinkEntity(url); + final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.save(referenceLink); + + // when + final OpenGraph openGraph = openGraphService.createOpenGraph(referenceLinkEntity, url); + + // then + assertAll( + () -> assertThat(openGraphRepository.findAll()).hasSize(1), + () -> assertThat(openGraph) + .extracting("headTitle", "openGraphTitle", "description", "image") + .contains("localhost", DEFAULT_OPEN_GRAPH_VALUE, DEFAULT_OPEN_GRAPH_VALUE, + DEFAULT_OPEN_GRAPH_VALUE) + ); + } + + @DisplayName("링크의 오픈그래프 타이틀, 헤드타이틀이 없으면 헤드타이틀에 도메인을 넣어 생성 후 저장한다.") + @Test + void create_openGraph_when_titles_are_empty() throws IOException { + //given + final String html = "" + + "" + + "" + + ""; + final int assignedPort = FakeServer.createAndStartFakeServer(html); + + final URL url = new URL("http://localhost:" + assignedPort + "/test"); + final ReferenceLinkEntity referenceLink = generateReferenceLinkEntity(url); + final ReferenceLinkEntity referenceLinkEntity = referenceLinkRepository.save(referenceLink); + + // when + final OpenGraph openGraph = openGraphService.createOpenGraph(referenceLinkEntity, url); + + // then + assertAll( + () -> assertThat(openGraphRepository.findAll()).hasSize(1), + () -> assertThat(openGraph.getHeadTitle()).isEqualTo("localhost"), + () -> assertThat(openGraph.getOpenGraphTitle()).isEqualTo(DEFAULT_OPEN_GRAPH_VALUE), + () -> assertThat(openGraph.getDescription()).isEqualTo("오픈그래프 설명"), + () -> assertThat(openGraph.getImage()).isEqualTo("오픈그래프 이미지") + ); + assertAll( + () -> assertThat(openGraphRepository.findAll()).hasSize(1), + () -> assertThat(openGraph) + .extracting("headTitle", "openGraphTitle", "description", "image") + .contains("localhost", DEFAULT_OPEN_GRAPH_VALUE, "오픈그래프 설명", + "오픈그래프 이미지") + ); + } + + private ReferenceLinkEntity generateReferenceLinkEntity(final URL url) { + return new ReferenceLinkEntity( + new ReferenceLink(url, new AccessCode(pairRoomEntity.getAccessCode())), + category, + pairRoomEntity + ); + } } diff --git a/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java b/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java index e209a7c4a..66bb0f48f 100644 --- a/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java +++ b/backend/src/test/java/site/coduo/referencelink/service/ReferenceLinkServiceTest.java @@ -1,6 +1,7 @@ package site.coduo.referencelink.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import static site.coduo.fixture.PairRoomFixture.INK_REDDDY_ROOM; @@ -10,6 +11,7 @@ import java.util.List; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +22,8 @@ import site.coduo.pairroom.repository.PairRoomRepository; import site.coduo.referencelink.domain.Category; import site.coduo.referencelink.domain.ReferenceLink; +import site.coduo.referencelink.exception.InvalidUrlFormatException; +import site.coduo.referencelink.fake.FakeServer; import site.coduo.referencelink.repository.CategoryEntity; import site.coduo.referencelink.repository.CategoryRepository; import site.coduo.referencelink.repository.OpenGraphRepository; @@ -47,6 +51,17 @@ class ReferenceLinkServiceTest extends CascadeCleaner { @Autowired private CategoryRepository categoryRepository; + private PairRoomEntity pairRoomEntity; + private CategoryEntity reactCategory; + private CategoryEntity springCategory; + + @BeforeEach + void setUp() { + pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); + reactCategory = categoryRepository.save(new CategoryEntity(pairRoomEntity, new Category("리액트"))); + springCategory = categoryRepository.save(new CategoryEntity(pairRoomEntity, new Category("스프링"))); + } + @AfterEach void tearDown() { deleteAllPairRoomCascade(); @@ -56,8 +71,8 @@ void tearDown() { @DisplayName("레퍼런스 링크와 오픈그래프를 함께 저장한다.") void save_reference_link_and_open_graph() { // given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest("https://www.naver.com", null); + final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest( + FakeServer.testUrl, springCategory.getId()); // when referenceLinkService.createReferenceLink(pairRoomEntity.getAccessCode(), request); @@ -69,34 +84,37 @@ void save_reference_link_and_open_graph() { () -> { final ReferenceLinkResponse referenceLinkResponses = referenceLinkService.readAllReferenceLink(pairRoomEntity.getAccessCode()).get(0); - assertThat(referenceLinkResponses.url()).isEqualTo(request.url()); - assertThat(referenceLinkResponses.headTitle()).isEqualTo("NAVER"); - assertThat(referenceLinkResponses.openGraphTitle()).isEqualTo("네이버"); + assertThat(referenceLinkResponses) + .extracting("url", "headTitle", "openGraphTitle", "description", "image", "categoryName") + .contains(request.url(), "헤드 타이틀", "오픈그래프 타이틀", "오픈그래프 설명", "오픈그래프 이미지", "스프링"); } ); } + @Test + @DisplayName("잘못된 url로 저장을 시도하면 예외가 발생한다.") + void throw_exception_when_invalid_url_format() { + // given + final ReferenceLinkCreateRequest request = new ReferenceLinkCreateRequest("failUrl", null); + + // when & then + assertThatThrownBy( + () -> referenceLinkService.createReferenceLink(pairRoomEntity.getAccessCode(), request)) + .isInstanceOf(InvalidUrlFormatException.class); + } + @Test @DisplayName("모든 레퍼런스 링크를 조회한다.") void search_all_reference_link() throws MalformedURLException { // given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final CategoryEntity category = categoryRepository.save(new CategoryEntity(pairRoomEntity, new Category("자바"))); final AccessCode accessCode = new AccessCode(pairRoomEntity.getAccessCode()); - referenceLinkRepository.save( - new ReferenceLinkEntity(new ReferenceLink(new URL("http://url1.com"), accessCode), category, - pairRoomEntity)); - referenceLinkRepository.save( - new ReferenceLinkEntity(new ReferenceLink(new URL("http://url2.com"), accessCode), category, - pairRoomEntity)); - referenceLinkRepository.save( - new ReferenceLinkEntity(new ReferenceLink(new URL("http://url3.com"), accessCode), category, - pairRoomEntity)); + referenceLinkRepository.save(generateReferenceLink(springCategory)); + referenceLinkRepository.save(generateReferenceLink(springCategory)); + referenceLinkRepository.save(generateReferenceLink(reactCategory)); // when final List responses = referenceLinkService.readAllReferenceLink( accessCode.getValue()); - // then assertThat(responses).hasSize(3); } @@ -105,16 +123,11 @@ void search_all_reference_link() throws MalformedURLException { @DisplayName("레퍼런스 링크와 오픈그래프를 삭제한다.") void delete_reference_link_and_open_graph() throws MalformedURLException { // given - final PairRoomEntity pairRoomEntity = pairRoomRepository.save(PairRoomEntity.from(INK_REDDDY_ROOM)); - final CategoryEntity category = categoryRepository.save( - new CategoryEntity(pairRoomEntity, new Category("리액트"))); - final ReferenceLinkEntity link = referenceLinkRepository.save( - new ReferenceLinkEntity( - new ReferenceLink(new URL("http://url1.com"), new AccessCode(pairRoomEntity.getAccessCode())), - category, pairRoomEntity)); + final ReferenceLinkEntity referenceLink = generateReferenceLink(reactCategory); + final ReferenceLinkEntity saved = referenceLinkRepository.save(referenceLink); // when - referenceLinkService.deleteReferenceLink(link.getId()); + referenceLinkService.deleteReferenceLink(pairRoomEntity.getAccessCode(), saved.getId()); // then assertAll( @@ -122,4 +135,48 @@ void delete_reference_link_and_open_graph() throws MalformedURLException { () -> assertThat(openGraphRepository.findAll()).isEmpty() ); } + + @DisplayName("액세스코드가 일치하지 않으면 삭제를 시도해도 삭제되지 않는다.") + @Test + void cannot_delete_reference_link_and_open_graph_when_invalid_access_code() throws MalformedURLException { + // given + final ReferenceLinkCreateRequest request = + new ReferenceLinkCreateRequest(FakeServer.testUrl, springCategory.getId()); + final ReferenceLinkResponse referenceLink = referenceLinkService.createReferenceLink( + pairRoomEntity.getAccessCode(), request); + + // when + referenceLinkService.deleteReferenceLink("abcdef", referenceLink.id()); + + assertAll( + () -> assertThat(referenceLinkRepository.findAll()).hasSize(1), + () -> assertThat(openGraphRepository.findAll()).hasSize(1) + ); + } + + @Test + @DisplayName("카테고리가 일치하는 모든 레퍼런스 링크를 조회한다.") + void find_reference_links_by_category() throws MalformedURLException { + // given + final ReferenceLinkEntity reactReferenceLink = generateReferenceLink(reactCategory); + final ReferenceLinkEntity reactReferenceLink2 = generateReferenceLink(reactCategory); + final ReferenceLinkEntity springReferenceLink = generateReferenceLink(springCategory); + + referenceLinkRepository.save(reactReferenceLink); + referenceLinkRepository.save(reactReferenceLink2); + referenceLinkRepository.save(springReferenceLink); + + // when + final List referenceLinksByCategory = referenceLinkService.findReferenceLinksByCategory( + pairRoomEntity.getAccessCode(), reactCategory.getId()); + + // then + assertThat(referenceLinksByCategory).hasSize(2); + } + + private ReferenceLinkEntity generateReferenceLink(final CategoryEntity category) throws MalformedURLException { + return new ReferenceLinkEntity(new ReferenceLink(new URL(FakeServer.testUrl), + new AccessCode(pairRoomEntity.getAccessCode())), + category, pairRoomEntity); + } } From 0eb5ccc99b2e5d3720f1f9ded24fef3e6061b66c Mon Sep 17 00:00:00 2001 From: anttiey Date: Tue, 8 Oct 2024 15:51:00 +0900 Subject: [PATCH 63/74] =?UTF-8?q?:recycle:=20=EC=97=94=ED=84=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=EC=9C=BC=EB=A1=9C=20=EA=B3=84=EC=A0=95=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A4=EA=B8=B0=20=EC=A0=9C=EC=96=B4=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/SignUp/SignUp.styles.ts | 37 +++++++++------------- frontend/src/pages/SignUp/SignUp.tsx | 16 ++++++---- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/frontend/src/pages/SignUp/SignUp.styles.ts b/frontend/src/pages/SignUp/SignUp.styles.ts index e461d193b..faf310ab1 100644 --- a/frontend/src/pages/SignUp/SignUp.styles.ts +++ b/frontend/src/pages/SignUp/SignUp.styles.ts @@ -1,13 +1,17 @@ -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; + +export const buttonStyles = css` + font-size: ${({ theme }) => theme.fontSize.md}; +`; export const Layout = styled.div` display: flex; flex-direction: column; align-items: center; - gap: 2rem; + gap: 6rem; height: calc(100vh - 7rem); - padding: 20px; + padding: 15rem 5rem; background-color: ${({ theme }) => theme.color.black[20]}; `; @@ -15,28 +19,17 @@ export const Layout = styled.div` export const LogoIconWithTitle = styled.img` width: 30rem; max-width: 40rem; - margin: 5rem; `; -export const Title = styled.h1` - margin-bottom: 2rem; - - color: ${({ theme }) => theme.color.primary[800]}; - font-size: ${({ theme }) => theme.fontSize.h5}; - font-weight: ${({ theme }) => theme.fontWeight.bold}; -`; - -export const InputWrapper = styled.div` - width: 100%; - max-width: 40rem; - margin-bottom: 2rem; -`; - -export const ButtonWrapper = styled.div` +export const Form = styled.form` display: flex; - justify-content: center; + flex-direction: column; align-items: center; + gap: 3.6rem; +`; - width: 100%; - max-width: 40rem; +export const Title = styled.h1` + color: ${({ theme }) => theme.color.primary[800]}; + font-size: ${({ theme }) => theme.fontSize.h5}; + font-weight: ${({ theme }) => theme.fontWeight.medium}; `; diff --git a/frontend/src/pages/SignUp/SignUp.tsx b/frontend/src/pages/SignUp/SignUp.tsx index ceec3ff04..c829ed459 100644 --- a/frontend/src/pages/SignUp/SignUp.tsx +++ b/frontend/src/pages/SignUp/SignUp.tsx @@ -37,25 +37,29 @@ const SignUp = () => { onUsernameChange(event, validateName(event.target.value)); }; + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + handleSignUp(username); + }; + return ( - 첫 방문이시네요! 당신을 어떻게 불러야 할까요? - + + 첫 방문이시네요! 당신을 어떻게 불러야 할까요? - - - - + ); }; From 3a387af1bc99a1cb84345987bf9c2dc2aa19e4d5 Mon Sep 17 00:00:00 2001 From: anttiey Date: Tue, 8 Oct 2024 15:53:45 +0900 Subject: [PATCH 64/74] =?UTF-8?q?:recycle:=20=EB=A0=88=ED=8D=BC=EB=9F=B0?= =?UTF-8?q?=EC=8A=A4=20=EA=B3=B5=EB=B0=B1=EB=A7=8C=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EC=8B=9C=20=ED=99=9C=EC=84=B1=ED=99=94=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReferenceCard/AddReferenceForm/AddReferenceForm.tsx | 2 +- .../CategoryManagementModal/CategoryManagementModal.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/PairRoom/ReferenceCard/AddReferenceForm/AddReferenceForm.tsx b/frontend/src/components/PairRoom/ReferenceCard/AddReferenceForm/AddReferenceForm.tsx index e8ac22eb0..38152f953 100644 --- a/frontend/src/components/PairRoom/ReferenceCard/AddReferenceForm/AddReferenceForm.tsx +++ b/frontend/src/components/PairRoom/ReferenceCard/AddReferenceForm/AddReferenceForm.tsx @@ -47,7 +47,7 @@ const AddReferenceForm = ({ accessCode, categories, getCategoryNameById, isCateg type="submit" size="sm" rounded={true} - disabled={value === '' || status !== 'DEFAULT'} + disabled={value.trim() === '' || status !== 'DEFAULT'} > diff --git a/frontend/src/components/PairRoom/ReferenceCard/CategoryManagementModal/CategoryManagementModal.tsx b/frontend/src/components/PairRoom/ReferenceCard/CategoryManagementModal/CategoryManagementModal.tsx index 5fd981cd8..e52658f63 100644 --- a/frontend/src/components/PairRoom/ReferenceCard/CategoryManagementModal/CategoryManagementModal.tsx +++ b/frontend/src/components/PairRoom/ReferenceCard/CategoryManagementModal/CategoryManagementModal.tsx @@ -82,7 +82,7 @@ const CategoryManagementModal = ({ type="submit" size="sm" rounded={true} - disabled={value === '' || status !== 'DEFAULT'} + disabled={value.trim() === '' || status !== 'DEFAULT'} > From b14c365dbdfa8a8b38d87cb65a201090f79d3122 Mon Sep 17 00:00:00 2001 From: anttiey Date: Tue, 8 Oct 2024 16:01:28 +0900 Subject: [PATCH 65/74] =?UTF-8?q?:bug:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=EC=97=91=EC=84=B8=EC=8A=A4=20=EC=BD=94=EB=93=9C=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EC=8B=9C=20=EC=97=90=EB=9F=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/PairRoom/PairRoom.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/frontend/src/pages/PairRoom/PairRoom.tsx b/frontend/src/pages/PairRoom/PairRoom.tsx index 8684bf02e..07a376c0e 100644 --- a/frontend/src/pages/PairRoom/PairRoom.tsx +++ b/frontend/src/pages/PairRoom/PairRoom.tsx @@ -9,7 +9,7 @@ import ReferenceCard from '@/components/PairRoom/ReferenceCard/ReferenceCard'; import TimerCard from '@/components/PairRoom/TimerCard/TimerCard'; import TodoListCard from '@/components/PairRoom/TodoListCard/TodoListCard'; -import { getPairRoom } from '@/apis/pairRoom'; +import { getPairRoomExists } from '@/apis/pairRoom'; import useGetPairRoom from '@/queries/PairRoom/useGetPairRoom'; import useUpdatePairRoom from '@/queries/PairRoom/useUpdatePairRoom'; @@ -18,18 +18,15 @@ import * as S from './PairRoom.styles'; const PairRoom = () => { const navigate = useNavigate(); - const { accessCode } = useParams(); useEffect(() => { const checkPairRoomExists = async () => { - if (!accessCode) return navigate('/404'); + if (!accessCode) navigate('/error'); + + const { exists } = await getPairRoomExists(accessCode || ''); - try { - await getPairRoom(accessCode); - } catch (error) { - navigate('/404'); - } + if (!exists) navigate('/error'); }; checkPairRoomExists(); From ee9e59a78dc8376929f6355c30ed6e776ac36455 Mon Sep 17 00:00:00 2001 From: greetings1012 Date: Tue, 8 Oct 2024 16:21:37 +0900 Subject: [PATCH 66/74] =?UTF-8?q?=F0=9F=9A=91=EF=B8=8F=20Coduodocs=20?= =?UTF-8?q?=EB=84=98=EB=B2=84=EB=A7=81=EC=9D=B4=20=EB=B0=80=EB=A6=B0=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/CoduoDocs/CoduoDocs.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/CoduoDocs/CoduoDocs.tsx b/frontend/src/pages/CoduoDocs/CoduoDocs.tsx index 0b2c328db..a6e0e315e 100644 --- a/frontend/src/pages/CoduoDocs/CoduoDocs.tsx +++ b/frontend/src/pages/CoduoDocs/CoduoDocs.tsx @@ -145,7 +145,7 @@ ex) cd java-guessing-number" Date: Wed, 9 Oct 2024 14:04:36 +0900 Subject: [PATCH 67/74] =?UTF-8?q?fix:=20DB=20=EC=BB=A4=EB=84=A5=EC=85=98?= =?UTF-8?q?=20=EA=B3=A0=EA=B0=88=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application-prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index 5e51c6e59..ac8d94932 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -21,6 +21,7 @@ spring: hibernate: ddl-auto: ${DDL_AUTO} database-platform: org.hibernate.dialect.MySQLDialect + open-in-view: false springdoc: swagger-ui: From 12043f3d2b1d9d5c5534a18035577e9eaa737e04 Mon Sep 17 00:00:00 2001 From: yechop Date: Fri, 11 Oct 2024 14:33:15 +0900 Subject: [PATCH 68/74] =?UTF-8?q?feat:=20=ED=8E=98=EC=96=B4=EB=A3=B8=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=EB=AF=B8=EC=85=98=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/docs/PairRoomDocs.java | 2 +- .../coduo/pairroom/domain/MissionUrl.java | 13 +++++ .../site/coduo/pairroom/domain/PairRoom.java | 13 +++-- .../pairroom/repository/PairRoomEntity.java | 26 +++++++--- .../pairroom/service/PairRoomService.java | 4 +- .../service/dto/PairRoomCreateRequest.java | 5 ++ .../service/dto/PairRoomReadResponse.java | 8 ++- .../acceptance/CategoryAcceptanceTest.java | 9 ++-- .../acceptance/PairRoomAcceptanceTest.java | 12 +++-- .../acceptance/ReferenceAcceptanceTest.java | 7 +-- .../coduo/acceptance/SseAcceptanceTest.java | 3 +- .../coduo/acceptance/TimerAcceptanceTest.java | 12 +++-- .../site/coduo/fixture/PairRoomFixture.java | 4 ++ .../pairroom/domain/PairRoomEntityTest.java | 4 +- .../PairRoomEntityRepositoryTest.java | 13 +++-- .../pairroom/service/PairRoomServiceTest.java | 12 +++-- .../site/coduo/timer/domain/TimerTest.java | 2 + .../timer/repository/TimerRepositoryTest.java | 2 + .../coduo/timer/service/TimerServiceTest.java | 20 ++++---- .../coduo/todo/service/TodoServiceTest.java | 49 ++++++++++--------- 20 files changed, 146 insertions(+), 74 deletions(-) create mode 100644 backend/src/main/java/site/coduo/pairroom/domain/MissionUrl.java diff --git a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java index f2727b273..6d52eaa8f 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java @@ -35,7 +35,7 @@ ResponseEntity getPairRoom( @ApiResponse(responseCode = "201", description = "페어룸 저장 성공", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = PairRoomCreateResponse.class))) ResponseEntity createPairRoom( - @Parameter(description = "페어 프로그래밍에 참여하는 드라이버 이름, 내비게이터 이름, 타이머 시간, 타이머 남은 시간", required = true) + @Parameter(description = "페어 프로그래밍에 참여하는 드라이버 이름, 내비게이터 이름, 타이머 시간, 타이머 남은 시간, 미션 리포지토리 링크", required = true) PairRoomCreateRequest pairRoomCreateRequest, @Parameter(description = "로그인 유저 토큰") String token diff --git a/backend/src/main/java/site/coduo/pairroom/domain/MissionUrl.java b/backend/src/main/java/site/coduo/pairroom/domain/MissionUrl.java new file mode 100644 index 000000000..214221d6b --- /dev/null +++ b/backend/src/main/java/site/coduo/pairroom/domain/MissionUrl.java @@ -0,0 +1,13 @@ +package site.coduo.pairroom.domain; + +import lombok.Getter; + +@Getter +public class MissionUrl { + + private final String value; + + public MissionUrl(final String value) { + this.value = value; + } +} diff --git a/backend/src/main/java/site/coduo/pairroom/domain/PairRoom.java b/backend/src/main/java/site/coduo/pairroom/domain/PairRoom.java index a73ba084a..95db98539 100644 --- a/backend/src/main/java/site/coduo/pairroom/domain/PairRoom.java +++ b/backend/src/main/java/site/coduo/pairroom/domain/PairRoom.java @@ -12,6 +12,7 @@ public class PairRoom { private final PairRoomStatus status; private final Pair pair; + private final MissionUrl missionUrl; private final AccessCode accessCode; public String getAccessCodeText() { @@ -26,22 +27,25 @@ public String getDriverName() { return pair.getDriverName(); } + public String getMissionUrl() { + return missionUrl.getValue(); + } + @Override public boolean equals(final Object o) { if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { + if (!(o instanceof final PairRoom pairRoom)) { return false; } - final PairRoom pairRoom = (PairRoom) o; return status == pairRoom.status && Objects.equals(pair, pairRoom.pair) && Objects.equals( - accessCode, pairRoom.accessCode); + missionUrl, pairRoom.missionUrl) && Objects.equals(accessCode, pairRoom.accessCode); } @Override public int hashCode() { - return Objects.hash(status, pair, accessCode); + return Objects.hash(status, pair, missionUrl, accessCode); } @Override @@ -49,6 +53,7 @@ public String toString() { return "PairRoom{" + "status=" + status + ", pair=" + pair + + ", missionUrl=" + missionUrl + ", accessCode=" + accessCode + '}'; } diff --git a/backend/src/main/java/site/coduo/pairroom/repository/PairRoomEntity.java b/backend/src/main/java/site/coduo/pairroom/repository/PairRoomEntity.java index fa2252c5e..555f8e045 100644 --- a/backend/src/main/java/site/coduo/pairroom/repository/PairRoomEntity.java +++ b/backend/src/main/java/site/coduo/pairroom/repository/PairRoomEntity.java @@ -16,6 +16,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import site.coduo.common.infrastructure.audit.entity.BaseTimeEntity; +import site.coduo.pairroom.domain.MissionUrl; import site.coduo.pairroom.domain.Pair; import site.coduo.pairroom.domain.PairName; import site.coduo.pairroom.domain.PairRoom; @@ -43,16 +44,24 @@ public class PairRoomEntity extends BaseTimeEntity { @Column(name = "DRIVER", nullable = false) private String driver; + @Column(name = "MISSION_URL", nullable = false) + private String missionUrl; + @Column(name = "ACCESS_CODE", nullable = false, unique = true) private String accessCode; @Builder - private PairRoomEntity(final Long id, final PairRoomStatus status, final String navigator, final String driver, + private PairRoomEntity(final Long id, + final PairRoomStatus status, + final String navigator, + final String driver, + final String missionUrl, final String accessCode) { this.id = id; this.status = status; this.navigator = navigator; this.driver = driver; + this.missionUrl = missionUrl; this.accessCode = accessCode; } @@ -62,6 +71,7 @@ public static PairRoomEntity from(final PairRoom pairRoom) { pairRoom.getStatus(), pairRoom.getNavigatorName(), pairRoom.getDriverName(), + pairRoom.getMissionUrl(), pairRoom.getAccessCodeText() ); } @@ -70,6 +80,7 @@ public PairRoom toDomain() { return new PairRoom( status, new Pair(new PairName(navigator), new PairName(driver)), + new MissionUrl(missionUrl), new AccessCode(accessCode) ); } @@ -104,11 +115,12 @@ public int hashCode() { @Override public String toString() { return "PairRoomEntity{" + - "id=" + id + - ", status=" + status + - ", navigator='" + navigator + '\'' + - ", driver='" + driver + '\'' + - ", accessCode='" + accessCode + '\'' + - '}'; + "id=" + id + + ", status=" + status + + ", navigator='" + navigator + '\'' + + ", driver='" + driver + '\'' + + ", missionUrl='" + missionUrl + '\'' + + ", accessCode='" + accessCode + '\'' + + '}'; } } diff --git a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java index bec2afc18..f3e704735 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java +++ b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java @@ -11,6 +11,7 @@ import lombok.extern.slf4j.Slf4j; import site.coduo.member.domain.Member; import site.coduo.member.service.MemberService; +import site.coduo.pairroom.domain.MissionUrl; import site.coduo.pairroom.domain.Pair; import site.coduo.pairroom.domain.PairName; import site.coduo.pairroom.domain.PairRoom; @@ -63,7 +64,8 @@ private PairRoom createPairRoom(final PairRoomCreateRequest request) { final AccessCode accessCode = generateAccessCode(); final PairRoomStatus status = PairRoomStatus.findByName(request.status()); final Pair pair = new Pair(new PairName(request.navigator()), new PairName(request.driver())); - return new PairRoom(status, pair, accessCode); + final MissionUrl missionUrl = new MissionUrl(request.missionUrl()); + return new PairRoom(status, pair, missionUrl, accessCode); } private AccessCode generateAccessCode() { diff --git a/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomCreateRequest.java b/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomCreateRequest.java index 52d0b189e..68ac00b80 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomCreateRequest.java +++ b/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomCreateRequest.java @@ -2,6 +2,7 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import io.swagger.v3.oas.annotations.media.Schema; @@ -23,6 +24,10 @@ public record PairRoomCreateRequest( @Min(value = 1, message = "타이머 남은 시간은 0보다 커야합니다.") long timerRemainingTime, + @Schema(description = "미션 리포지토리 링크. '그냥 시작할래요'로 생성하면 빈 문자열") + @NotNull + String missionUrl, + @Schema(description = "페어룸의 상태", example = "IN_PROGRESS") @NotBlank String status diff --git a/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomReadResponse.java b/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomReadResponse.java index bc0f77d80..0b70198e4 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomReadResponse.java +++ b/backend/src/main/java/site/coduo/pairroom/service/dto/PairRoomReadResponse.java @@ -19,7 +19,10 @@ public record PairRoomReadResponse( long duration, @Schema(description = "타이머 남은 시간 (millisecond 기준)", example = "5000") - long remainingTime + long remainingTime, + + @Schema(description = "미션 리포지토리 링크", example = "https://github.com/coduo-missions/coduo-javascript-rps") + String missionUrl ) { public static PairRoomReadResponse of(final PairRoom pairRoom, final Timer timer) { @@ -28,7 +31,8 @@ public static PairRoomReadResponse of(final PairRoom pairRoom, final Timer timer pairRoom.getDriverName(), pairRoom.getStatus().name(), timer.getDuration(), - timer.getRemainingTime() + timer.getRemainingTime(), + pairRoom.getMissionUrl() ); } } diff --git a/backend/src/test/java/site/coduo/acceptance/CategoryAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/CategoryAcceptanceTest.java index 5252ebdac..2a14ffbb4 100644 --- a/backend/src/test/java/site/coduo/acceptance/CategoryAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/CategoryAcceptanceTest.java @@ -39,7 +39,7 @@ static CategoryCreateResponse createCategory(final String accessCode, final Cate void show_category() { //given final PairRoomCreateResponse pairRoomUrl = PairRoomAcceptanceTest.createPairRoom( - new PairRoomCreateRequest("레디", "프람", 10000L, 10000L, + new PairRoomCreateRequest("레디", "프람", 10000L, 10000L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name())); createCategory(pairRoomUrl.accessCode(), new CategoryCreateRequest("새로운 카테고리")); @@ -65,14 +65,15 @@ void show_category() { void update_category() { //given final PairRoomCreateResponse pairRoomUrl = PairRoomAcceptanceTest.createPairRoom( - new PairRoomCreateRequest("레디", "프람", 10000L, 10000L, + new PairRoomCreateRequest("레디", "프람", 10000L, 10000L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name())); final CategoryCreateResponse previousCategory = createCategory(pairRoomUrl.accessCode(), new CategoryCreateRequest("이전 카테고리")); final String updateName = "변경된 카테고리"; - final CategoryUpdateRequest request = new CategoryUpdateRequest(Long.parseLong(previousCategory.id()), updateName); + final CategoryUpdateRequest request = new CategoryUpdateRequest(Long.parseLong(previousCategory.id()), + updateName); //when & then final CategoryUpdateResponse categoryUpdateResponse = RestAssured @@ -100,7 +101,7 @@ void update_category() { void delete_category() { //given final PairRoomCreateResponse pairRoomUrl = PairRoomAcceptanceTest.createPairRoom( - new PairRoomCreateRequest("레디", "프람", 10000L, 10000L, + new PairRoomCreateRequest("레디", "프람", 10000L, 10000L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name())); final CategoryCreateResponse category = createCategory(pairRoomUrl.accessCode(), diff --git a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java index 7dab4e66c..fbe753c36 100644 --- a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java @@ -37,7 +37,8 @@ static PairRoomCreateResponse createPairRoom(final PairRoomCreateRequest request void show_pair_room() { //given final PairRoomCreateResponse pairRoomUrl = - createPairRoom(new PairRoomCreateRequest("레디", "프람", 10000L, 10000L, "IN_PROGRESS")); + createPairRoom( + new PairRoomCreateRequest("레디", "프람", 10000L, 10000L, "https://missionUrl.xxx", "IN_PROGRESS")); //when & then RestAssured @@ -60,7 +61,8 @@ void show_pair_room() { void update_pair_room_status() { //given final PairRoomCreateResponse accessCode = - createPairRoom(new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "IN_PROGRESS")); + createPairRoom( + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "https://missionUrl.xxx", "IN_PROGRESS")); final Map status = Map.of("status", PairRoomStatus.IN_PROGRESS.name()); // when & then @@ -85,7 +87,8 @@ void update_pair_room_status() { void update_driver_navigator() { // given final PairRoomCreateResponse accessCode = - createPairRoom(new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "IN_PROGRESS")); + createPairRoom( + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "https://missionUrl.xxx", "IN_PROGRESS")); // when & then RestAssured @@ -105,7 +108,8 @@ void update_driver_navigator() { void exist_pair_room_true() { //given final PairRoomCreateResponse accessCode = - createPairRoom(new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "IN_PROGRESS")); + createPairRoom( + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "https://missionUrl.xxx", "IN_PROGRESS")); // when & then final PairRoomExistResponse response = RestAssured diff --git a/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java index 1a7d638ff..cfd5e0e29 100644 --- a/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java @@ -28,7 +28,7 @@ void reference_link_create_request() { // given final PairRoomCreateResponse pairRoom = createPairRoom(new PairRoomCreateRequest("레모네", "프람", 10000L, 10000L, - "IN_PROGRESS")); + "https://missionUrl.xxx", "IN_PROGRESS")); final CategoryCreateResponse category = CategoryAcceptanceTest.createCategory( pairRoom.accessCode(), new CategoryCreateRequest("타입스크립트")); @@ -56,7 +56,7 @@ void read_all_reference_link_request() { // given final PairRoomCreateResponse pairRoom = createPairRoom(new PairRoomCreateRequest("레모네", "프람", 10000L, 10000L, - "IN_PROGRESS")); + "https://missionUrl.xxx", "IN_PROGRESS")); createReferenceLink("http://www.some1.url", pairRoom.accessCode(), "카테고리1"); createReferenceLink("http://www.some2.url", pairRoom.accessCode(), "카테고리2"); @@ -79,7 +79,8 @@ void read_all_reference_link_request() { void read_reference_link_without_open_graph() { // given final PairRoomCreateResponse pairRoom = - createPairRoom(new PairRoomCreateRequest("잉크", "해시", 1000L, 100L, "IN_PROGRESS")); + createPairRoom( + new PairRoomCreateRequest("잉크", "해시", 1000L, 100L, "https://missionUrl.xxx", "IN_PROGRESS")); final String expectedUrl = "http://www.deleasfsdte.com"; createReferenceLink(expectedUrl, pairRoom.accessCode(), "카테고리"); diff --git a/backend/src/test/java/site/coduo/acceptance/SseAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/SseAcceptanceTest.java index 4e4543b79..09e2a8817 100644 --- a/backend/src/test/java/site/coduo/acceptance/SseAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/SseAcceptanceTest.java @@ -29,7 +29,7 @@ static void createConnect(final String accessCode) { void create_sse_connection() { // given final PairRoomCreateRequest request = new PairRoomCreateRequest("프람", "레모네", 10000L, - 10000L, PairRoomStatus.IN_PROGRESS.name()); + 10000L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()); final String accessCode = createPairRoom(request).accessCode(); // when & then @@ -54,6 +54,7 @@ void delete_sse_connection() { "잉크", 1000L, 1000L, + "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name() ); final String accessCode = createPairRoom(request).accessCode(); diff --git a/backend/src/test/java/site/coduo/acceptance/TimerAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/TimerAcceptanceTest.java index 5c8b4aca7..b0d3330c3 100644 --- a/backend/src/test/java/site/coduo/acceptance/TimerAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/TimerAcceptanceTest.java @@ -52,6 +52,7 @@ void get_timer() { "파란", 10000L, 10000L, + "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()) ); @@ -75,6 +76,7 @@ void update_timer_duration() { "파슬리", 10000L, 10000L, + "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()) ); final TimerUpdateRequest request = new TimerUpdateRequest(20000L, 3000L); @@ -96,8 +98,9 @@ void update_timer_duration() { @DisplayName("타이머를 시작한다.") void start_timer() { // given - final String accessCode = createPairRoom(new PairRoomCreateRequest("fram", "lemone", 10000L, 10000L, - PairRoomStatus.IN_PROGRESS.name())); + final String accessCode = createPairRoom( + new PairRoomCreateRequest("fram", "lemone", 10000L, 10000L, "https://missionUrl.xxx", + PairRoomStatus.IN_PROGRESS.name())); createConnect(accessCode); // when & then @@ -115,8 +118,9 @@ void start_timer() { @DisplayName("타이머를 종료한다.") void stop_timer() { // given - final String accessCode = createPairRoom(new PairRoomCreateRequest("fram", "lemone", 10000L, 10000L, - PairRoomStatus.IN_PROGRESS.name())); + final String accessCode = createPairRoom( + new PairRoomCreateRequest("fram", "lemone", 10000L, 10000L, "https://missionUrl.xxx", + PairRoomStatus.IN_PROGRESS.name())); timerStart(accessCode); // when & then diff --git a/backend/src/test/java/site/coduo/fixture/PairRoomFixture.java b/backend/src/test/java/site/coduo/fixture/PairRoomFixture.java index 00fa062c6..c214b14f6 100644 --- a/backend/src/test/java/site/coduo/fixture/PairRoomFixture.java +++ b/backend/src/test/java/site/coduo/fixture/PairRoomFixture.java @@ -4,6 +4,7 @@ import static site.coduo.fixture.AccessCodeFixture.ALPHABET_ACCESS_CODE; import static site.coduo.fixture.AccessCodeFixture.NUMBER_ACCESS_CODE; +import site.coduo.pairroom.domain.MissionUrl; import site.coduo.pairroom.domain.Pair; import site.coduo.pairroom.domain.PairName; import site.coduo.pairroom.domain.PairRoom; @@ -17,6 +18,7 @@ public class PairRoomFixture { new PairName("잉크"), new PairName("레디") ), + new MissionUrl("https://github.com/coduo-missions/coduo-javascript-rps"), ACCESS_CODE); public static final PairRoom FRAM_LEMONE_ROOM = new PairRoom( @@ -25,6 +27,7 @@ public class PairRoomFixture { new PairName("프람"), new PairName("레모네") ), + new MissionUrl("https://github.com/coduo-missions/coduo-javascript-rps"), ALPHABET_ACCESS_CODE); public static final PairRoom KELY_LEMONE_ROOM = new PairRoom( @@ -33,6 +36,7 @@ public class PairRoomFixture { new PairName("켈리"), new PairName("레모네") ), + new MissionUrl("https://github.com/coduo-missions/coduo-javascript-rps"), NUMBER_ACCESS_CODE ); } diff --git a/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java b/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java index d3771592d..6875595e7 100644 --- a/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java +++ b/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java @@ -21,9 +21,10 @@ void create_pair_room() { final String secondName = "second"; final Pair pair = new Pair(new PairName(firstName), new PairName(secondName)); final PairRoomStatus pairRoomStatus = PairRoomStatus.IN_PROGRESS; + final MissionUrl missionUrl = new MissionUrl("https://missionUrl.xxx"); // when & then - assertThatCode(() -> new PairRoom(pairRoomStatus, pair, ACCESS_CODE)) + assertThatCode(() -> new PairRoom(pairRoomStatus, pair, missionUrl, ACCESS_CODE)) .doesNotThrowAnyException(); } @@ -34,6 +35,7 @@ void change_nav_and_driver() { final PairRoomEntity sut = PairRoomEntity.from( new PairRoom(PairRoomStatus.IN_PROGRESS, new Pair(new PairName("navi"), new PairName("dri")), + new MissionUrl("https://missionUrl.xxx"), new AccessCode("access")) ); diff --git a/backend/src/test/java/site/coduo/pairroom/repository/PairRoomEntityRepositoryTest.java b/backend/src/test/java/site/coduo/pairroom/repository/PairRoomEntityRepositoryTest.java index 76bf53e8f..49d6ee153 100644 --- a/backend/src/test/java/site/coduo/pairroom/repository/PairRoomEntityRepositoryTest.java +++ b/backend/src/test/java/site/coduo/pairroom/repository/PairRoomEntityRepositoryTest.java @@ -10,6 +10,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; +import site.coduo.pairroom.domain.MissionUrl; import site.coduo.pairroom.domain.Pair; import site.coduo.pairroom.domain.PairName; import site.coduo.pairroom.domain.PairRoom; @@ -28,9 +29,9 @@ class PairRoomEntityRepositoryTest { void search_persistence_by_access_code_exists_case() { // given final Pair pair = new Pair(new PairName("hello"), new PairName("world")); - final PairRoom pairRoom = new PairRoom(PairRoomStatus.IN_PROGRESS, pair, new AccessCode("code")); - final PairRoomEntity entity = PairRoomEntity.from( - pairRoom); + final MissionUrl missionUrl = new MissionUrl("https://missionUrl.xxx"); + final PairRoom pairRoom = new PairRoom(PairRoomStatus.IN_PROGRESS, pair, missionUrl, new AccessCode("code")); + final PairRoomEntity entity = PairRoomEntity.from(pairRoom); pairRoomRepository.save(entity); // when @@ -46,7 +47,8 @@ void search_persistence_by_access_code_exists_case() { void search_persistence_by_access_code_not_exists_case() { // given final Pair pair = new Pair(new PairName("hello"), new PairName("world")); - final PairRoom pairRoom = new PairRoom(PairRoomStatus.IN_PROGRESS, pair, new AccessCode("code")); + final MissionUrl missionUrl = new MissionUrl("https://missionUrl.xxx"); + final PairRoom pairRoom = new PairRoom(PairRoomStatus.IN_PROGRESS, pair, missionUrl, new AccessCode("code")); // when final Optional persistence = pairRoomRepository.findByAccessCode( @@ -62,7 +64,8 @@ void search_persistence_by_access_code_domain_exists_case() { // given final Pair pair = new Pair(new PairName("hello"), new PairName("world")); final AccessCode code = new AccessCode("code"); - final PairRoom pairRoom = new PairRoom(PairRoomStatus.IN_PROGRESS, pair, code); + final MissionUrl missionUrl = new MissionUrl("https://missionUrl.xxx"); + final PairRoom pairRoom = new PairRoom(PairRoomStatus.IN_PROGRESS, pair, missionUrl, code); pairRoomRepository.save(PairRoomEntity.from(pairRoom)); // when diff --git a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java index b02c50705..913c2910b 100644 --- a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java +++ b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java @@ -17,6 +17,7 @@ import site.coduo.member.domain.Member; import site.coduo.member.domain.repository.MemberRepository; import site.coduo.member.infrastructure.security.JwtProvider; +import site.coduo.pairroom.domain.MissionUrl; import site.coduo.pairroom.domain.Pair; import site.coduo.pairroom.domain.PairName; import site.coduo.pairroom.domain.PairRoom; @@ -52,7 +53,7 @@ class PairRoomServiceTest { void create_pair_room() { // given final PairRoomCreateRequest request = - new PairRoomCreateRequest("레디", "프람", 1000L, 100L, + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()); // when @@ -68,7 +69,7 @@ void create_pair_room() { void create_timer_when_create_pair_room() { // given final PairRoomCreateRequest request = - new PairRoomCreateRequest("레디", "프람", 1000L, 100L, + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()); // when @@ -96,7 +97,8 @@ void throw_exception_when_find_not_exist_access_code() { void update_pair_room_status() { // given final PairRoomCreateRequest request = - new PairRoomCreateRequest("레디", "프람", 1000L, 100L, PairRoomStatus.IN_PROGRESS.name()); + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "https://missionUrl.xxx", + PairRoomStatus.IN_PROGRESS.name()); final String accessCode = pairRoomService.savePairRoom(request, null); // when @@ -114,6 +116,7 @@ void change_pair_room() { final PairRoomEntity entity = PairRoomEntity.from( new PairRoom(PairRoomStatus.IN_PROGRESS, new Pair(new PairName("fram"), new PairName("lemonL")), + new MissionUrl("https://missionUrl.xxx"), new AccessCode("1234")) ); pairRoomRepository.save(entity); @@ -136,6 +139,7 @@ void find_rooms_by_member() { final Member memberB = createMember("test"); final PairRoomCreateRequest pairRoomCreateRequest = new PairRoomCreateRequest("레디", "잉크", 1, 1, + "https://missionUrl.xxx", "IN_PROGRESS"); final String accessCodeA_1 = pairRoomService.savePairRoom(pairRoomCreateRequest, memberA.getAccessToken()); @@ -182,6 +186,7 @@ void get_pair_room_and_timer() { final PairRoomEntity pairRoomEntity = PairRoomEntity.from( new PairRoom(PairRoomStatus.IN_PROGRESS, new Pair(new PairName("레디"), new PairName("파슬리")), + new MissionUrl("https://missionUrl.xxx"), new AccessCode("123456")) ); final Timer timer = new Timer( @@ -211,6 +216,7 @@ void exists_pair_room() { final PairRoomEntity pairRoomEntity = PairRoomEntity.from( new PairRoom(PairRoomStatus.IN_PROGRESS, new Pair(new PairName("레디"), new PairName("레모네")), + new MissionUrl("https://missionUrl.xxx"), accessCode )); pairRoomRepository.save(pairRoomEntity); diff --git a/backend/src/test/java/site/coduo/timer/domain/TimerTest.java b/backend/src/test/java/site/coduo/timer/domain/TimerTest.java index 8529cecde..acd5e7638 100644 --- a/backend/src/test/java/site/coduo/timer/domain/TimerTest.java +++ b/backend/src/test/java/site/coduo/timer/domain/TimerTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import site.coduo.pairroom.domain.MissionUrl; import site.coduo.pairroom.domain.Pair; import site.coduo.pairroom.domain.PairName; import site.coduo.pairroom.domain.PairRoom; @@ -74,6 +75,7 @@ private PairRoom createPairRoom(final String navigator, final String driver) { return new PairRoom( PairRoomStatus.IN_PROGRESS, new Pair(new PairName(navigator), new PairName(driver)), + new MissionUrl("https://missionUrl.xxx"), new AccessCode("123456") ); } diff --git a/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java b/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java index 29acaa9b8..f527d8e00 100644 --- a/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java +++ b/backend/src/test/java/site/coduo/timer/repository/TimerRepositoryTest.java @@ -9,6 +9,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; +import site.coduo.pairroom.domain.MissionUrl; import site.coduo.pairroom.domain.Pair; import site.coduo.pairroom.domain.PairName; import site.coduo.pairroom.domain.PairRoom; @@ -41,6 +42,7 @@ void inquiry_timer() { final PairRoom pairRoom = new PairRoom( PairRoomStatus.IN_PROGRESS, new Pair(new PairName("레머네"), new PairName("프람")), + new MissionUrl("https://missionUrl.xxx"), new AccessCode("hello1") ); final PairRoomEntity entity = site.coduo.pairroom.repository.PairRoomEntity.from( diff --git a/backend/src/test/java/site/coduo/timer/service/TimerServiceTest.java b/backend/src/test/java/site/coduo/timer/service/TimerServiceTest.java index 98b17062c..cdad8f90b 100644 --- a/backend/src/test/java/site/coduo/timer/service/TimerServiceTest.java +++ b/backend/src/test/java/site/coduo/timer/service/TimerServiceTest.java @@ -42,8 +42,8 @@ void tearDown() { @DisplayName("타이머를 저장한다.") void create_timer() { // given - final PairRoomCreateRequest request = new PairRoomCreateRequest("켈리", "레모네", 10000L, 1000L, - PairRoomStatus.IN_PROGRESS.name()); + final PairRoomCreateRequest request = new PairRoomCreateRequest("켈리", "레모네", 10000L, + 1000L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()); // when & then assertThatCode(() -> pairRoomService.savePairRoom(request, null)) @@ -54,8 +54,8 @@ void create_timer() { @DisplayName("타이머를 반환한다.") void get_latest_timer() { // given - final PairRoomCreateRequest request = new PairRoomCreateRequest("잉크", "레디", 1000L, 1000L, - PairRoomStatus.IN_PROGRESS.name()); + final PairRoomCreateRequest request = new PairRoomCreateRequest("잉크", "레디", 1000L, + 1000L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()); final String accessCode = pairRoomService.savePairRoom(request, null); // when @@ -72,8 +72,8 @@ void get_latest_timer() { @DisplayName("타이머를 업데이트 한다.") void update_timer() { // given - final PairRoomCreateRequest request = new PairRoomCreateRequest("잉크", "레디", 10000000L, 100L, - PairRoomStatus.IN_PROGRESS.name()); + final PairRoomCreateRequest request = new PairRoomCreateRequest("잉크", "레디", 10000000L, + 100L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()); final String accessCode = pairRoomService.savePairRoom(request, null); final TimerUpdateRequest timerRequest = new TimerUpdateRequest(10000, 5000); @@ -92,8 +92,8 @@ void update_timer() { @DisplayName("타이머 남은 시간을 반환한다. - 타이머 타임 스탬프가 존재할 경우") void get_remaining_time_when_exist_timestamp() { // given - final PairRoomCreateRequest pairRoomCreateRequest = new PairRoomCreateRequest("켈리", "레모네", 3000L, 3000L, - PairRoomStatus.IN_PROGRESS.name()); + final PairRoomCreateRequest pairRoomCreateRequest = new PairRoomCreateRequest("켈리", "레모네", + 3000L, 3000L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()); final String accessCode = pairRoomService.savePairRoom(pairRoomCreateRequest, null); final Timer timeStamp = new Timer(new AccessCode(accessCode), 10000L, 10000L); timestampRegistry.register(accessCode, timeStamp); @@ -109,8 +109,8 @@ void get_remaining_time_when_exist_timestamp() { @DisplayName("타이머 남은 시간을 반환한다. - 타이머가 한번도 동작하지 않았을 경우") void get_remaining_time_when_not_exist_timestamp() { // given - final PairRoomCreateRequest pairRoomCreateRequest = new PairRoomCreateRequest("켈리", "레모네", 3000L, 3000L, - PairRoomStatus.IN_PROGRESS.name()); + final PairRoomCreateRequest pairRoomCreateRequest = new PairRoomCreateRequest("켈리", "레모네", + 3000L, 3000L, "https://missionUrl.xxx", PairRoomStatus.IN_PROGRESS.name()); final String accessCode = pairRoomService.savePairRoom(pairRoomCreateRequest, null); // when diff --git a/backend/src/test/java/site/coduo/todo/service/TodoServiceTest.java b/backend/src/test/java/site/coduo/todo/service/TodoServiceTest.java index 9e165e728..a09b6a475 100644 --- a/backend/src/test/java/site/coduo/todo/service/TodoServiceTest.java +++ b/backend/src/test/java/site/coduo/todo/service/TodoServiceTest.java @@ -45,6 +45,19 @@ class TodoServiceTest { @Autowired private TodoService todoService; + private static Stream destinationSortAndExpectOrder() { + return Stream.of( + Arguments.of(0, List.of("content4!", "content1!", "content2!", "content3!", "content5!", "content6!", + "content7!")), + Arguments.of(6, List.of("content1!", "content2!", "content3!", "content5!", "content6!", "content7!", + "content4!")), + Arguments.of(1, List.of("content1!", "content4!", "content2!", "content3!", "content5!", "content6!", + "content7!")), + Arguments.of(5, List.of("content1!", "content2!", "content3!", "content5!", "content6!", "content4!", + "content7!")) + ); + } + @AfterEach void clean() { todoRepository.deleteAll(); @@ -56,7 +69,7 @@ void clean() { void createTodo() { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final String content = "content!"; // When @@ -77,7 +90,8 @@ void createTodo() { @Test void createPairRoomWithNotFoundPairRoomId() { // Given - pairRoomService.savePairRoom(new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + pairRoomService.savePairRoom( + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final String content = "content!"; // When & Then @@ -91,7 +105,7 @@ void createPairRoomWithNotFoundPairRoomId() { void updateTodoContent() { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); final String content = "content!"; @@ -119,7 +133,7 @@ void updateTodoContent() { void updateTodoContentWithNotFoundTodoId() { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); final String content = "content!"; @@ -143,7 +157,7 @@ void updateTodoContentWithNotFoundTodoId() { void toggleTodoChecked() { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); final String content = "content!"; @@ -169,7 +183,7 @@ void toggleTodoChecked() { void toggleTodoCheckedWithNotFoundTodoId() { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); final String content = "content!"; @@ -192,7 +206,7 @@ void toggleTodoCheckedWithNotFoundTodoId() { void deleteTodo() { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); final String content = "content!"; @@ -219,7 +233,7 @@ void deleteTodo() { void getAll() { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); final List todos = List.of( @@ -256,7 +270,7 @@ void getAll() { void getAllOrderBySortWithNotExistPairRoomId() { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); final List todos = List.of( @@ -284,7 +298,7 @@ void getAllOrderBySortWithNotExistPairRoomId() { void updateTodoSort(final int destinationSort, final List expect) { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); final List todos = List.of( @@ -316,25 +330,12 @@ void updateTodoSort(final int destinationSort, final List expect) { assertThat(orders).isEqualTo(expect); } - private static Stream destinationSortAndExpectOrder() { - return Stream.of( - Arguments.of(0, List.of("content4!", "content1!", "content2!", "content3!", "content5!", "content6!", - "content7!")), - Arguments.of(6, List.of("content1!", "content2!", "content3!", "content5!", "content6!", "content7!", - "content4!")), - Arguments.of(1, List.of("content1!", "content4!", "content2!", "content3!", "content5!", "content6!", - "content7!")), - Arguments.of(5, List.of("content1!", "content2!", "content3!", "content5!", "content6!", "content4!", - "content7!")) - ); - } - @DisplayName("존재하지 않은 페어룸 아이디와 함께 투두 순서 변경 요청을 하면 예외를 발생시킨다.") @Test void updateTodoSortWithNotExistPairRoomId() { // Given final String accessCode = pairRoomService.savePairRoom( - new PairRoomCreateRequest("A", "B", 60_000, 60_000, "IN_PROGRESS"), null); + new PairRoomCreateRequest("A", "B", 60_000, 60_000, "https://missionUrl.xxx", "IN_PROGRESS"), null); final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); final List todos = List.of( From 40b669db7dd8fe1e0ab4a3312939034e4546211b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Fri, 11 Oct 2024 14:52:27 +0900 Subject: [PATCH 69/74] =?UTF-8?q?fix:=20nginx=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=ED=95=B4=EB=8D=94=20=EC=B6=94=EA=B0=80=20(#758)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/site/coduo/sync/controller/SseController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/site/coduo/sync/controller/SseController.java b/backend/src/main/java/site/coduo/sync/controller/SseController.java index 4abc5ffa3..74207e6ec 100644 --- a/backend/src/main/java/site/coduo/sync/controller/SseController.java +++ b/backend/src/main/java/site/coduo/sync/controller/SseController.java @@ -23,7 +23,9 @@ public class SseController implements SseDocs { public ResponseEntity createConnection(@PathVariable("key") final String key) { final SseEmitter sseEmitter = sseService.connect(key); - return ResponseEntity.ok(sseEmitter); + return ResponseEntity.ok() + .header("X-Accel-Buffering", "no") + .body(sseEmitter); } @DeleteMapping("/{key}/connect") From b1db266a37e202de05b6bbeeb3f67df30b1adb05 Mon Sep 17 00:00:00 2001 From: "Sunguk Yang (Kelly)" Date: Fri, 11 Oct 2024 16:00:17 +0900 Subject: [PATCH 70/74] =?UTF-8?q?[BE]=20=ED=8E=98=EC=96=B4=EB=A3=B8=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#753)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 페어룸 삭제 기능 구현 기존 페어룸 조회 기능에 삭제된 페어룸을 검증하는 로직도 추가함. * test: 페어룸 삭제 API 테스트 추가 * test: 실패하는 테스트 임시 비활성화 처리 * fix: 링크 삭제 테스트 id 하드코딩된 것 변경 * test: 불필요 @Transactional 삭제 * refactor: 어법에 맞지 않은 enum 네이밍 수정 * test: enum 네이밍 수정으로 인한 test 코드 수정 --------- Co-authored-by: fram1998 Co-authored-by: Redddy <78539407+reddevilmidzy@users.noreply.github.com> --- .../controller/PairRoomController.java | 7 +++ .../controller/docs/PairRoomDocs.java | 7 +++ .../coduo/pairroom/domain/PairRoomStatus.java | 3 +- .../exception/DeletePairRoomException.java | 8 +++ .../pairroom/repository/PairRoomEntity.java | 16 +++--- .../repository/PairRoomRepository.java | 3 ++ .../pairroom/service/PairRoomService.java | 30 +++++++++-- .../acceptance/PairRoomAcceptanceTest.java | 20 +++++++ .../acceptance/ReferenceAcceptanceTest.java | 18 ++++--- .../pairroom/domain/PairRoomEntityTest.java | 34 ++++++++++++ .../pairroom/service/PairRoomServiceTest.java | 53 ++++++++++++++++++- 11 files changed, 178 insertions(+), 21 deletions(-) create mode 100644 backend/src/main/java/site/coduo/pairroom/exception/DeletePairRoomException.java diff --git a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java index c27bc6200..3d953dcbf 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/PairRoomController.java @@ -9,6 +9,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -92,4 +93,10 @@ public ResponseEntity pairRoomExists(@RequestParam("acces return ResponseEntity.ok(response); } + + @DeleteMapping("/pair-room/{accessCode}") + public ResponseEntity deletePairRoom(@PathVariable("accessCode") final String accessCode) { + pairRoomService.deletePairRoom(accessCode); + return ResponseEntity.noContent().build(); + } } diff --git a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java index f2727b273..693efb483 100644 --- a/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java +++ b/backend/src/main/java/site/coduo/pairroom/controller/docs/PairRoomDocs.java @@ -74,4 +74,11 @@ ResponseEntity> getPairRooms( @ApiResponse(responseCode = "200", description = "페어룸 존재 여부", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = PairRoomExistResponse.class))) ResponseEntity pairRoomExists(String accessCode); + + @Operation(summary = "페어룸을 삭제한다.") + @ApiResponse(responseCode = "204", description = "페어룸 삭제 성공") + ResponseEntity deletePairRoom( + @Parameter(description = "페어룸 접근 코드", required = true) + String accessCode + ); } diff --git a/backend/src/main/java/site/coduo/pairroom/domain/PairRoomStatus.java b/backend/src/main/java/site/coduo/pairroom/domain/PairRoomStatus.java index 7500dc46f..06a6d5b4a 100644 --- a/backend/src/main/java/site/coduo/pairroom/domain/PairRoomStatus.java +++ b/backend/src/main/java/site/coduo/pairroom/domain/PairRoomStatus.java @@ -12,7 +12,8 @@ public enum PairRoomStatus { IN_PROGRESS, - COMPLETED; + COMPLETED, + DELETED; private static final Map STATUS = Arrays.stream(values()) .collect(Collectors.toMap(PairRoomStatus::name, Function.identity())); diff --git a/backend/src/main/java/site/coduo/pairroom/exception/DeletePairRoomException.java b/backend/src/main/java/site/coduo/pairroom/exception/DeletePairRoomException.java new file mode 100644 index 000000000..a470d3ed4 --- /dev/null +++ b/backend/src/main/java/site/coduo/pairroom/exception/DeletePairRoomException.java @@ -0,0 +1,8 @@ +package site.coduo.pairroom.exception; + +public class DeletePairRoomException extends PairRoomException { + + public DeletePairRoomException(final String message) { + super(message); + } +} diff --git a/backend/src/main/java/site/coduo/pairroom/repository/PairRoomEntity.java b/backend/src/main/java/site/coduo/pairroom/repository/PairRoomEntity.java index fa2252c5e..717cb5e31 100644 --- a/backend/src/main/java/site/coduo/pairroom/repository/PairRoomEntity.java +++ b/backend/src/main/java/site/coduo/pairroom/repository/PairRoomEntity.java @@ -84,6 +84,10 @@ public void swapNavigatorWithDriver() { this.driver = temp; } + public boolean isDelete() { + return status == PairRoomStatus.DELETED; + } + @Override public boolean equals(final Object o) { if (this == o) { @@ -104,11 +108,11 @@ public int hashCode() { @Override public String toString() { return "PairRoomEntity{" + - "id=" + id + - ", status=" + status + - ", navigator='" + navigator + '\'' + - ", driver='" + driver + '\'' + - ", accessCode='" + accessCode + '\'' + - '}'; + "id=" + id + + ", status=" + status + + ", navigator='" + navigator + '\'' + + ", driver='" + driver + '\'' + + ", accessCode='" + accessCode + '\'' + + '}'; } } diff --git a/backend/src/main/java/site/coduo/pairroom/repository/PairRoomRepository.java b/backend/src/main/java/site/coduo/pairroom/repository/PairRoomRepository.java index 9ca969bac..9f1bec184 100644 --- a/backend/src/main/java/site/coduo/pairroom/repository/PairRoomRepository.java +++ b/backend/src/main/java/site/coduo/pairroom/repository/PairRoomRepository.java @@ -4,6 +4,7 @@ import org.springframework.data.jpa.repository.JpaRepository; +import site.coduo.pairroom.domain.PairRoomStatus; import site.coduo.pairroom.domain.accesscode.AccessCode; import site.coduo.pairroom.exception.PairRoomNotFoundException; @@ -22,4 +23,6 @@ default PairRoomEntity fetchByAccessCode(AccessCode accessCode) { } boolean existsByAccessCode(String generatedAccessCode); + + boolean existsByAccessCodeAndStatusNot(String accessCode, PairRoomStatus status); } diff --git a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java index bec2afc18..cbdc5163e 100644 --- a/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java +++ b/backend/src/main/java/site/coduo/pairroom/service/PairRoomService.java @@ -17,6 +17,7 @@ import site.coduo.pairroom.domain.PairRoomStatus; import site.coduo.pairroom.domain.accesscode.AccessCode; import site.coduo.pairroom.domain.accesscode.UUIDAccessCodeGenerator; +import site.coduo.pairroom.exception.DeletePairRoomException; import site.coduo.pairroom.repository.PairRoomEntity; import site.coduo.pairroom.repository.PairRoomMemberEntity; import site.coduo.pairroom.repository.PairRoomMemberRepository; @@ -56,7 +57,7 @@ public String savePairRoom(final PairRoomCreateRequest request, @Nullable final } public boolean existsByAccessCode(final String accessCode) { - return pairRoomRepository.existsByAccessCode(accessCode); + return pairRoomRepository.existsByAccessCodeAndStatusNot(accessCode, PairRoomStatus.DELETED); } private PairRoom createPairRoom(final PairRoomCreateRequest request) { @@ -77,18 +78,27 @@ private AccessCode generateAccessCode() { @Transactional public void updateNavigatorWithDriver(final String accessCode) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); + checkDeletePairRoom(pairRoomEntity); pairRoomEntity.swapNavigatorWithDriver(); } + private void checkDeletePairRoom(final PairRoomEntity pairRoomEntity) { + if (pairRoomEntity.isDelete()) { + throw new DeletePairRoomException("삭제된 페어룸입니다."); + } + } + @Transactional public void updatePairRoomStatus(final String accessCode, final String statusName) { + final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); + checkDeletePairRoom(pairRoomEntity); final PairRoomStatus status = PairRoomStatus.findByName(statusName); - final PairRoomEntity entity = pairRoomRepository.fetchByAccessCode(accessCode); - entity.updateStatus(status); + pairRoomEntity.updateStatus(status); } public PairRoomReadResponse findPairRoomAndTimer(final String accessCode) { final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); + checkDeletePairRoom(pairRoomEntity); final TimerEntity timerEntity = timerRepository.fetchTimerByPairRoomEntity(pairRoomEntity); return PairRoomReadResponse.of(pairRoomEntity.toDomain(), timerEntity.toDomain()); } @@ -97,10 +107,20 @@ public List findPairRooms(final String token) { final Member member = memberService.findMemberByCredential(token); final List pairRooms = pairRoomMemberRepository.findByMember(member); - - return pairRooms.stream() + final List pairRoomEntities = pairRooms.stream() .map(PairRoomMemberEntity::getPairRoom) + .filter(pairRoomEntity -> !pairRoomEntity.isDelete()) + .toList(); + + return pairRoomEntities.stream() .map(PairRoomMemberResponse::from) .toList(); } + + @Transactional + public void deletePairRoom(final String accessCode) { + final PairRoomEntity pairRoomEntity = pairRoomRepository.fetchByAccessCode(accessCode); + checkDeletePairRoom(pairRoomEntity); + pairRoomEntity.updateStatus(PairRoomStatus.DELETED); + } } diff --git a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java index 7dab4e66c..0129bb4e2 100644 --- a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java @@ -151,4 +151,24 @@ void exist_pair_room_false() { assertThat(response.exists()).isFalse(); } + + @Test + @DisplayName("페어룸을 삭제한다.") + void delete_pair_room() { + // given + final PairRoomCreateResponse accessCode = + createPairRoom(new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "IN_PROGRESS")); + + // when & then + RestAssured + .given() + .log() + .all() + + .when() + .delete("/api/pair-room/{access-code}", accessCode.accessCode()) + + .then() + .statusCode(204); + } } diff --git a/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java index 1a7d638ff..2523a3436 100644 --- a/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; -import org.springframework.transaction.annotation.Transactional; import io.restassured.RestAssured; import io.restassured.http.ContentType; @@ -18,8 +17,8 @@ import site.coduo.pairroom.service.dto.PairRoomCreateResponse; import site.coduo.referencelink.service.dto.CategoryCreateRequest; import site.coduo.referencelink.service.dto.CategoryCreateResponse; +import site.coduo.referencelink.service.dto.ReferenceLinkResponse; -@Transactional class ReferenceAcceptanceTest extends AcceptanceFixture { @Test @@ -101,18 +100,22 @@ void read_reference_link_without_open_graph() { .body("[0].image", is("")); } - void createReferenceLink(final String url, String accessCodeText, String categoryName) { + ReferenceLinkResponse createReferenceLink(final String url, String accessCodeText, String categoryName) { final CategoryCreateResponse response = CategoryAcceptanceTest.createCategory( accessCodeText, new CategoryCreateRequest(categoryName)); final Map request = Map.of("url", url, "categoryId", response.id()); - RestAssured + return RestAssured .given() .contentType(ContentType.JSON) .body(request) .when() - .post("/api/" + accessCodeText + "/reference-link"); + .post("/api/" + accessCodeText + "/reference-link") + + .then() + .extract() + .as(ReferenceLinkResponse.class); } @Test @@ -122,7 +125,8 @@ void delete_reference_link_request() { final PairRoomCreateResponse pairRoom = createPairRoom(new PairRoomCreateRequest("레모네", "프람", 1000L, 1000L, "IN_PROGRESS")); - createReferenceLink("http://www.delete.com", pairRoom.accessCode(), "카테고리 이름"); + final ReferenceLinkResponse response = createReferenceLink("http://www.delete.com", pairRoom.accessCode(), + "카테고리 이름"); // when & then RestAssured @@ -131,7 +135,7 @@ void delete_reference_link_request() { .when() .log().all() - .delete("/api/" + pairRoom.accessCode() + "/reference-link/1") + .delete("/api/" + pairRoom.accessCode() + "/reference-link/" + response.id()) .then() .assertThat() diff --git a/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java b/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java index d3771592d..ac5aa35e9 100644 --- a/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java +++ b/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java @@ -45,4 +45,38 @@ void change_nav_and_driver() { .extracting("navigator", "driver") .contains("dri", "navi"); } + + @Test + @DisplayName("페어룸 상태가 DELETE면 true를 반환한다.") + void pairRoomEntityStatusIsDelete() { + // Given + final PairRoomEntity sut = PairRoomEntity.from( + new PairRoom(PairRoomStatus.DELETED, + new Pair(new PairName("navi"), new PairName("dri")), + new AccessCode("access")) + ); + + // When + final boolean isDelete = sut.isDelete(); + + // Then + assertThat(isDelete).isTrue(); + } + + @Test + @DisplayName("페어룸 상태가 DELETE가 아니면 false를 반환한다.") + void pairRoomEntityStatusIsNotDelete() { + // Given + final PairRoomEntity sut = PairRoomEntity.from( + new PairRoom(PairRoomStatus.IN_PROGRESS, + new Pair(new PairName("navi"), new PairName("dri")), + new AccessCode("access")) + ); + + // When + final boolean isDelete = sut.isDelete(); + + // Then + assertThat(isDelete).isFalse(); + } } diff --git a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java index b02c50705..d9ccec2ad 100644 --- a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java +++ b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java @@ -22,6 +22,7 @@ import site.coduo.pairroom.domain.PairRoom; import site.coduo.pairroom.domain.PairRoomStatus; import site.coduo.pairroom.domain.accesscode.AccessCode; +import site.coduo.pairroom.exception.DeletePairRoomException; import site.coduo.pairroom.exception.PairRoomNotFoundException; import site.coduo.pairroom.repository.PairRoomEntity; import site.coduo.pairroom.repository.PairRoomRepository; @@ -80,7 +81,6 @@ void create_timer_when_create_pair_room() { @Test - @Transactional @DisplayName("존재하지 않는 페어룸 접근 코드를 찾으면 예외가 발생한다.") void throw_exception_when_find_not_exist_access_code() { // given @@ -91,6 +91,20 @@ void throw_exception_when_find_not_exist_access_code() { .isExactlyInstanceOf(PairRoomNotFoundException.class); } + @Test + @DisplayName("삭제된 페어룸의 접근 코드를 찾으면 예외가 발생한다.") + void throw_exception_when_find_delete_pair_room_access_code() { + // given + final PairRoomCreateRequest request = + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, + PairRoomStatus.DELETED.name()); + final String accessCode = pairRoomService.savePairRoom(request, null); + + // when & then + assertThatThrownBy(() -> pairRoomService.findPairRoomAndTimer(accessCode)) + .isExactlyInstanceOf(DeletePairRoomException.class); + } + @Test @DisplayName("페어룸 상태를 변경한다.") void update_pair_room_status() { @@ -107,6 +121,19 @@ void update_pair_room_status() { .isEqualTo(PairRoomStatus.COMPLETED); } + @Test + @DisplayName("삭제된 페어룸 상태를 변경하려고 하면 예외를 발생시킨다.") + void update_delete_pair_room_status() { + // given + final PairRoomCreateRequest request = + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, PairRoomStatus.DELETED.name()); + final String accessCode = pairRoomService.savePairRoom(request, null); + + // when & then + assertThatThrownBy(() -> pairRoomService.updatePairRoomStatus(accessCode, PairRoomStatus.COMPLETED.name())) + .isExactlyInstanceOf(DeletePairRoomException.class); + } + @Test @DisplayName("페어 역할을 변경한다.") void change_pair_room() { @@ -127,8 +154,24 @@ void change_pair_room() { .contains("lemonL", "fram"); } + @Test + @DisplayName("삭제된 페어룸의 페어 역할을 변경하려하면 예외를 발생시킨다.") + void change_delete_pair_room_role() { + // given + final PairRoomEntity entity = PairRoomEntity.from( + new PairRoom(PairRoomStatus.DELETED, + new Pair(new PairName("fram"), new PairName("lemonL")), + new AccessCode("1234")) + ); + pairRoomRepository.save(entity); + + // when & then + assertThatThrownBy(() -> pairRoomService.updateNavigatorWithDriver(entity.getAccessCode())) + .isExactlyInstanceOf(DeletePairRoomException.class); + } + - @DisplayName("멤버의 방 목록을 가져온다.") + @DisplayName("삭제되지 않은, 멤버의 방 목록을 가져온다.") @Test void find_rooms_by_member() { //given @@ -143,6 +186,12 @@ void find_rooms_by_member() { final String accessCodeB_1 = pairRoomService.savePairRoom(pairRoomCreateRequest, memberB.getAccessToken()); pairRoomService.savePairRoom(pairRoomCreateRequest, null); + final PairRoomCreateRequest deletePairRoomCreateRequest = new PairRoomCreateRequest("레디", "잉크", 1, 1, + PairRoomStatus.DELETED.name()); + pairRoomService.savePairRoom(deletePairRoomCreateRequest, memberA.getAccessToken()); + pairRoomService.savePairRoom(deletePairRoomCreateRequest, memberA.getAccessToken()); + pairRoomService.savePairRoom(deletePairRoomCreateRequest, memberA.getAccessToken()); + final List memberAExpected = List.of(accessCodeA_1, accessCodeA_2); final List memberBExpected = List.of(accessCodeB_1); From e2bc7e4bff083a98bfc286d66bc080f3a4713da0 Mon Sep 17 00:00:00 2001 From: yechop Date: Fri, 11 Oct 2024 16:21:39 +0900 Subject: [PATCH 71/74] =?UTF-8?q?test:=20=EC=83=9D=EC=84=B1=EC=9E=90?= =?UTF-8?q?=EC=97=90=20missionUrl=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../site/coduo/acceptance/PairRoomAcceptanceTest.java | 3 ++- .../site/coduo/acceptance/ReferenceAcceptanceTest.java | 3 ++- .../site/coduo/pairroom/domain/PairRoomEntityTest.java | 2 ++ .../site/coduo/pairroom/service/PairRoomServiceTest.java | 8 +++++--- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java index 19ed9485d..f799d14bd 100644 --- a/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/PairRoomAcceptanceTest.java @@ -161,7 +161,8 @@ void exist_pair_room_false() { void delete_pair_room() { // given final PairRoomCreateResponse accessCode = - createPairRoom(new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "IN_PROGRESS")); + createPairRoom( + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "https://missionUrl.xxx", "IN_PROGRESS")); // when & then RestAssured diff --git a/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java index 00e0907bf..2b4c2db67 100644 --- a/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/ReferenceAcceptanceTest.java @@ -124,7 +124,8 @@ ReferenceLinkResponse createReferenceLink(final String url, String accessCodeTex void delete_reference_link_request() { // given final PairRoomCreateResponse pairRoom = - createPairRoom(new PairRoomCreateRequest("레모네", "프람", 1000L, 1000L, "IN_PROGRESS")); + createPairRoom( + new PairRoomCreateRequest("레모네", "프람", 1000L, 1000L, "https://missionUrl.xxx", "IN_PROGRESS")); final ReferenceLinkResponse response = createReferenceLink("http://www.delete.com", pairRoom.accessCode(), "카테고리 이름"); diff --git a/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java b/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java index 3b546e239..69818feb4 100644 --- a/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java +++ b/backend/src/test/java/site/coduo/pairroom/domain/PairRoomEntityTest.java @@ -55,6 +55,7 @@ void pairRoomEntityStatusIsDelete() { final PairRoomEntity sut = PairRoomEntity.from( new PairRoom(PairRoomStatus.DELETED, new Pair(new PairName("navi"), new PairName("dri")), + new MissionUrl("https://missionUrl.xxx"), new AccessCode("access")) ); @@ -72,6 +73,7 @@ void pairRoomEntityStatusIsNotDelete() { final PairRoomEntity sut = PairRoomEntity.from( new PairRoom(PairRoomStatus.IN_PROGRESS, new Pair(new PairName("navi"), new PairName("dri")), + new MissionUrl("https://missionUrl.xxx"), new AccessCode("access")) ); diff --git a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java index a4026772e..62a5939fa 100644 --- a/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java +++ b/backend/src/test/java/site/coduo/pairroom/service/PairRoomServiceTest.java @@ -98,7 +98,7 @@ void throw_exception_when_find_delete_pair_room_access_code() { // given final PairRoomCreateRequest request = new PairRoomCreateRequest("레디", "프람", 1000L, 100L, - PairRoomStatus.DELETED.name()); + "https://missionUrl.xxx", PairRoomStatus.DELETED.name()); final String accessCode = pairRoomService.savePairRoom(request, null); // when & then @@ -128,7 +128,8 @@ void update_pair_room_status() { void update_delete_pair_room_status() { // given final PairRoomCreateRequest request = - new PairRoomCreateRequest("레디", "프람", 1000L, 100L, PairRoomStatus.DELETED.name()); + new PairRoomCreateRequest("레디", "프람", 1000L, 100L, "https://missionUrl.xxx", + PairRoomStatus.DELETED.name()); final String accessCode = pairRoomService.savePairRoom(request, null); // when & then @@ -164,6 +165,7 @@ void change_delete_pair_room_role() { final PairRoomEntity entity = PairRoomEntity.from( new PairRoom(PairRoomStatus.DELETED, new Pair(new PairName("fram"), new PairName("lemonL")), + new MissionUrl("https://missionUrl.xxx"), new AccessCode("1234")) ); pairRoomRepository.save(entity); @@ -191,7 +193,7 @@ void find_rooms_by_member() { pairRoomService.savePairRoom(pairRoomCreateRequest, null); final PairRoomCreateRequest deletePairRoomCreateRequest = new PairRoomCreateRequest("레디", "잉크", 1, 1, - PairRoomStatus.DELETED.name()); + "https://missionUrl.xxx", PairRoomStatus.DELETED.name()); pairRoomService.savePairRoom(deletePairRoomCreateRequest, memberA.getAccessToken()); pairRoomService.savePairRoom(deletePairRoomCreateRequest, memberA.getAccessToken()); pairRoomService.savePairRoom(deletePairRoomCreateRequest, memberA.getAccessToken()); From 60a481837a3e6bbc2670eb579dfc64157cd2ef44 Mon Sep 17 00:00:00 2001 From: lemone Date: Fri, 11 Oct 2024 17:14:21 +0900 Subject: [PATCH 72/74] =?UTF-8?q?refactor:=20sse=20connection=EC=9D=84=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=ED=95=A0=20=EB=96=84=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EC=98=88=EC=99=B8=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EA=B7=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/site/coduo/sync/service/SseEventStream.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java index 7cf36a495..d026be3e0 100644 --- a/backend/src/main/java/site/coduo/sync/service/SseEventStream.java +++ b/backend/src/main/java/site/coduo/sync/service/SseEventStream.java @@ -60,8 +60,7 @@ public void flush(final String name, final String message) { .name(name) .data(message) ); - } catch (final IOException e) { - log.warn("SSE 통신 중 에러가 발생했습니다."); + } catch (final IOException ignored) { } } From 0b9d37c9189f4977c4a4905d2bbda342872cbbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A2=85?= Date: Mon, 14 Oct 2024 16:01:15 +0900 Subject: [PATCH 73/74] =?UTF-8?q?feat:=20Rest=20Client=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC=20(#767)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coduo/member/client/GithubApiClient.java | 19 + .../member/client/GithubOAuthClient.java | 15 + .../controller/MemberExceptionHandler.java | 9 + .../controller/error/MemberApiError.java | 3 +- .../exception/ExternalApiCallException.java | 8 + .../test/java/site/coduo/fake/FakeClient.java | 456 ++++++++++++++++++ .../member/client/GithubApiClientTest.java | 45 ++ 7 files changed, 554 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/site/coduo/member/exception/ExternalApiCallException.java create mode 100644 backend/src/test/java/site/coduo/fake/FakeClient.java create mode 100644 backend/src/test/java/site/coduo/member/client/GithubApiClientTest.java diff --git a/backend/src/main/java/site/coduo/member/client/GithubApiClient.java b/backend/src/main/java/site/coduo/member/client/GithubApiClient.java index f2272030b..354ffb38d 100644 --- a/backend/src/main/java/site/coduo/member/client/GithubApiClient.java +++ b/backend/src/main/java/site/coduo/member/client/GithubApiClient.java @@ -1,13 +1,16 @@ package site.coduo.member.client; +import org.springframework.http.HttpStatusCode; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.stereotype.Component; import org.springframework.web.client.RestClient; +import org.springframework.web.client.RestClient.ResponseSpec.ErrorHandler; import lombok.extern.slf4j.Slf4j; import site.coduo.member.client.dto.GithubUserRequest; import site.coduo.member.client.dto.GithubUserResponse; +import site.coduo.member.exception.ExternalApiCallException; @Slf4j @Component @@ -30,6 +33,10 @@ public GithubApiClient() { .build(); } + GithubApiClient(final RestClient restClient) { + this.client = restClient; + } + public GithubUserResponse getUser(final GithubUserRequest request) { return client.get() @@ -37,6 +44,18 @@ public GithubUserResponse getUser(final GithubUserRequest request) { .accept() .headers(httpHeaders -> httpHeaders.addAll(request.getHeaders())) .retrieve() + .onStatus(HttpStatusCode::isError, getErrorHandler()) .body(GithubUserResponse.class); } + + private ErrorHandler getErrorHandler() { + return (request, response) -> { + if (response.getStatusCode().is4xxClientError()) { + throw new ExternalApiCallException("Github API 호출에 실패했습니다."); + } + if (response.getStatusCode().is5xxServerError()) { + throw new ExternalApiCallException("Github API 호출 과정 중 서버 내부에서 오류가 발생했습니다."); + } + }; + } } diff --git a/backend/src/main/java/site/coduo/member/client/GithubOAuthClient.java b/backend/src/main/java/site/coduo/member/client/GithubOAuthClient.java index 2b8e6d038..985ac3f0a 100644 --- a/backend/src/main/java/site/coduo/member/client/GithubOAuthClient.java +++ b/backend/src/main/java/site/coduo/member/client/GithubOAuthClient.java @@ -2,14 +2,17 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.stereotype.Component; import org.springframework.web.client.RestClient; +import org.springframework.web.client.RestClient.ResponseSpec.ErrorHandler; import site.coduo.member.client.dto.TokenRequest; import site.coduo.member.client.dto.TokenResponse; +import site.coduo.member.exception.ExternalApiCallException; import site.coduo.member.infrastructure.http.Basic; @Component @@ -50,6 +53,7 @@ public TokenResponse grant(final TokenRequest request) { .header(HttpHeaders.AUTHORIZATION, new Basic(oAuthClientId, oAuthClientSecret).getValue()) .body(request.toQueryParams()) .retrieve() + .onStatus(HttpStatusCode::isError, getErrorHandler()) .body(TokenResponse.class); } @@ -60,4 +64,15 @@ public String getOAuthClientId() { public String getOAuthRedirectUri() { return oAuthRedirectUri; } + + private ErrorHandler getErrorHandler() { + return (request, response) -> { + if (response.getStatusCode().is4xxClientError()) { + throw new ExternalApiCallException("Github OAuth API 호출에 실패했습니다."); + } + if (response.getStatusCode().is5xxServerError()) { + throw new ExternalApiCallException("Github OAuth 과정 중 서버 내부에서 오류가 발생했습니다."); + } + }; + } } diff --git a/backend/src/main/java/site/coduo/member/controller/MemberExceptionHandler.java b/backend/src/main/java/site/coduo/member/controller/MemberExceptionHandler.java index 0f488d758..d0a244593 100644 --- a/backend/src/main/java/site/coduo/member/controller/MemberExceptionHandler.java +++ b/backend/src/main/java/site/coduo/member/controller/MemberExceptionHandler.java @@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j; import site.coduo.common.controller.response.ApiErrorResponse; import site.coduo.member.controller.error.MemberApiError; +import site.coduo.member.exception.ExternalApiCallException; import site.coduo.member.exception.MemberNotFoundException; @Slf4j @@ -23,4 +24,12 @@ public ResponseEntity handlerMemberNotFoundException(final Mem return ResponseEntity.status(MemberApiError.MEMBER_NOT_FOUND_ERROR.getHttpStatus()) .body(new ApiErrorResponse(MemberApiError.MEMBER_NOT_FOUND_ERROR.getMessage())); } + + @ExceptionHandler(ExternalApiCallException.class) + public ResponseEntity handlerExternalApiCallFailureException(final ExternalApiCallException e) { + log.error(e.getMessage()); + + return ResponseEntity.status(MemberApiError.API_CALL_FAILURE_ERROR.getHttpStatus()) + .body(new ApiErrorResponse(MemberApiError.MEMBER_NOT_FOUND_ERROR.getMessage())); + } } diff --git a/backend/src/main/java/site/coduo/member/controller/error/MemberApiError.java b/backend/src/main/java/site/coduo/member/controller/error/MemberApiError.java index a2a360f79..8e0cedc2c 100644 --- a/backend/src/main/java/site/coduo/member/controller/error/MemberApiError.java +++ b/backend/src/main/java/site/coduo/member/controller/error/MemberApiError.java @@ -10,7 +10,8 @@ public enum MemberApiError { AUTHENTICATION_ERROR(HttpStatus.UNAUTHORIZED, "인증되지 않은 접근입니다."), - MEMBER_NOT_FOUND_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다."); + MEMBER_NOT_FOUND_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다."), + API_CALL_FAILURE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "외부 API와 상호작용 중 실패했습니다."); private final HttpStatus httpStatus; private final String message; diff --git a/backend/src/main/java/site/coduo/member/exception/ExternalApiCallException.java b/backend/src/main/java/site/coduo/member/exception/ExternalApiCallException.java new file mode 100644 index 000000000..2a8554c7a --- /dev/null +++ b/backend/src/main/java/site/coduo/member/exception/ExternalApiCallException.java @@ -0,0 +1,8 @@ +package site.coduo.member.exception; + +public class ExternalApiCallException extends MemberException { + + public ExternalApiCallException(final String message) { + super(message); + } +} diff --git a/backend/src/test/java/site/coduo/fake/FakeClient.java b/backend/src/test/java/site/coduo/fake/FakeClient.java new file mode 100644 index 000000000..99e4087fd --- /dev/null +++ b/backend/src/test/java/site/coduo/fake/FakeClient.java @@ -0,0 +1,456 @@ +package site.coduo.fake; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.nio.charset.Charset; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.StreamingHttpOutputMessage.Body; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.ClientHttpRequestInitializer; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.client.observation.ClientRequestObservationConvention; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.mock.http.client.MockClientHttpRequest; +import org.springframework.mock.http.client.MockClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.RestClient; +import org.springframework.web.client.RestClient.ResponseSpec.ErrorHandler; +import org.springframework.web.util.UriBuilder; +import org.springframework.web.util.UriBuilderFactory; + +import io.micrometer.observation.ObservationRegistry; +import site.coduo.member.client.dto.GithubUserResponse; + +public class FakeClient implements RestClient { + + @Override + public RequestHeadersUriSpec get() { + return new FakeRequestHeaderUriSpec(); + } + + @Override + public RequestHeadersUriSpec head() { + return new FakeRequestHeaderUriSpec(); + } + + @Override + public RequestBodyUriSpec post() { + return new FakeRequestBodyBodyUriSpec(); + } + + @Override + public RequestBodyUriSpec put() { + return new FakeRequestBodyBodyUriSpec(); + } + + @Override + public RequestBodyUriSpec patch() { + return new FakeRequestBodyBodyUriSpec(); + } + + @Override + public RequestHeadersUriSpec delete() { + return new FakeRequestHeaderUriSpec(); + } + + @Override + public RequestHeadersUriSpec options() { + return new FakeRequestHeaderUriSpec(); + } + + @Override + public RequestBodyUriSpec method(final HttpMethod method) { + return new FakeRequestBodyBodyUriSpec(); + } + + @Override + public Builder mutate() { + return new FakeBuilder(); + } + + + class FakeBuilder implements Builder { + + @Override + public Builder baseUrl(final String baseUrl) { + return this; + } + + @Override + public Builder defaultUriVariables(final Map defaultUriVariables) { + return this; + } + + @Override + public Builder uriBuilderFactory(final UriBuilderFactory uriBuilderFactory) { + return this; + } + + @Override + public Builder defaultHeader(final String header, final String... values) { + return this; + } + + @Override + public Builder defaultHeaders(final Consumer headersConsumer) { + return this; + } + + @Override + public Builder defaultRequest(final Consumer> defaultRequest) { + return this; + } + + @Override + public Builder defaultStatusHandler(final Predicate statusPredicate, + final ErrorHandler errorHandler) { + return this; + } + + @Override + public Builder defaultStatusHandler(final ResponseErrorHandler errorHandler) { + return this; + } + + @Override + public Builder requestInterceptor(final ClientHttpRequestInterceptor interceptor) { + return this; + } + + @Override + public Builder requestInterceptors(final Consumer> interceptorsConsumer) { + return this; + } + + @Override + public Builder requestInitializer(final ClientHttpRequestInitializer initializer) { + return this; + } + + @Override + public Builder requestInitializers(final Consumer> initializersConsumer) { + return this; + } + + @Override + public Builder requestFactory(final ClientHttpRequestFactory requestFactory) { + return this; + } + + @Override + public Builder messageConverters(final Consumer>> configurer) { + return this; + } + + @Override + public Builder observationRegistry(final ObservationRegistry observationRegistry) { + return this; + } + + @Override + public Builder observationConvention(final ClientRequestObservationConvention observationConvention) { + return this; + } + + @Override + public Builder apply(final Consumer builderConsumer) { + return this; + } + + @Override + public Builder clone() { + return this; + } + + @Override + public RestClient build() { + return new FakeClient(); + } + } + + class FakeResponseSpec implements ResponseSpec { + + private final HttpRequest request; + private final ClientHttpResponse response; + private final HttpStatus httpStatus; + + public FakeResponseSpec(final HttpStatus httpStatus, final HttpMethod method, final URI url) { + this.httpStatus = httpStatus; + this.request = new MockClientHttpRequest(method, url); + this.response = new MockClientHttpResponse(new byte[]{}, httpStatus.value()); + } + + @Override + public ResponseSpec onStatus(final Predicate statusPredicate, final ErrorHandler errorHandler) { + final boolean error = statusPredicate.test(httpStatus); + if (error) { + try { + errorHandler.handle(request, response); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + return this; + } + + @Override + public ResponseSpec onStatus(final ResponseErrorHandler errorHandler) { + try { + errorHandler.handleError(response); + } catch (IOException e) { + throw new RuntimeException(e); + } + return this; + } + + @Override + public T body(final Class bodyType) { + if (bodyType.isInstance(new GithubUserResponse("", "", ""))) { + try { + return bodyType.getDeclaredConstructor(String.class, String.class, String.class) + .newInstance("userId", "login", "avatar_url"); + } catch (final NoSuchMethodException | InstantiationException | IllegalAccessException | + InvocationTargetException e) { + throw new RuntimeException(e); + } + } + return null; + } + + @Override + public T body(final ParameterizedTypeReference bodyType) { + return null; + } + + @Override + public ResponseEntity toEntity(final Class bodyType) { + return null; + } + + @Override + public ResponseEntity toEntity(final ParameterizedTypeReference bodyType) { + return null; + } + + @Override + public ResponseEntity toBodilessEntity() { + return null; + } + } + + class FakeRequestHeaderUriSpec implements RequestHeadersUriSpec { + + private final HttpHeaders headers = new HttpHeaders(); + private String endPoint; + + @Override + public RequestHeadersSpec accept(final MediaType... acceptableMediaTypes) { + Stream.of(acceptableMediaTypes) + .forEach(value -> headers.add(HttpHeaders.ACCEPT, value.toString())); + + return this; + } + + @Override + public RequestHeadersSpec acceptCharset(final Charset... acceptableCharsets) { + Stream.of(acceptableCharsets) + .forEach(value -> headers.add(HttpHeaders.ACCEPT_CHARSET, value.displayName())); + + return this; + } + + @Override + public RequestHeadersSpec ifModifiedSince(final ZonedDateTime ifModifiedSince) { + headers.add(HttpHeaders.IF_MODIFIED_SINCE, ifModifiedSince.toString()); + return this; + } + + @Override + public RequestHeadersSpec ifNoneMatch(final String... ifNoneMatches) { + Stream.of(ifNoneMatches) + .forEach(value -> headers.add(HttpHeaders.IF_NONE_MATCH, value)); + return this; + } + + @Override + public RequestHeadersSpec header(final String headerName, final String... headerValues) { + Stream.of(headerValues) + .forEach(value -> headers.add(headerName, value)); + return this; + } + + @Override + public RequestHeadersSpec headers(final Consumer consumer) { + consumer.accept(headers); + return this; + } + + @Override + public RequestHeadersSpec httpRequest(final Consumer consumer) { + return this; + } + + @Override + public ResponseSpec retrieve() { + if (endPoint.equals("/user")) { + final String token = headers.get(HttpHeaders.AUTHORIZATION).get(0).substring(7); + if (token.isBlank()) { + return new FakeResponseSpec(HttpStatus.INTERNAL_SERVER_ERROR, HttpMethod.GET, URI.create("/user")); + } + return new FakeResponseSpec(HttpStatus.OK, HttpMethod.GET, URI.create("/user")); + } + return null; + } + + @Override + public Object exchange(final ExchangeFunction exchangeFunction, final boolean close) { + return null; + } + + @Override + public RequestHeadersSpec uri(final URI uri) { + endPoint = uri.toString(); + return this; + } + + @Override + public RequestHeadersSpec uri(final String uri, final Object... uriVariables) { + endPoint = uri; + return this; + } + + @Override + public RequestHeadersSpec uri(final String uri, final Map uriVariables) { + endPoint = uri; + return this; + } + + @Override + public RequestHeadersSpec uri(final String uri, final Function function) { + endPoint = uri; + return this; + } + + @Override + public RequestHeadersSpec uri(final Function function) { + return this; + } + } + + class FakeRequestBodyBodyUriSpec implements RequestBodyUriSpec { + + @Override + public RequestBodySpec contentLength(final long contentLength) { + return this; + } + + @Override + public RequestBodySpec contentType(final MediaType contentType) { + return this; + } + + @Override + public RequestBodySpec body(final Object body) { + return this; + } + + @Override + public RequestBodySpec body(final T body, final ParameterizedTypeReference bodyType) { + return this; + } + + @Override + public RequestBodySpec body(final Body body) { + return this; + } + + @Override + public RequestBodySpec accept(final MediaType... acceptableMediaTypes) { + return this; + } + + @Override + public RequestBodySpec acceptCharset(final Charset... acceptableCharsets) { + return this; + } + + @Override + public RequestBodySpec ifModifiedSince(final ZonedDateTime ifModifiedSince) { + return this; + } + + @Override + public RequestBodySpec ifNoneMatch(final String... ifNoneMatches) { + return this; + } + + @Override + public RequestBodySpec header(final String headerName, final String... headerValues) { + return this; + } + + @Override + public RequestBodySpec headers(final Consumer headersConsumer) { + return this; + } + + @Override + public RequestBodySpec httpRequest(final Consumer requestConsumer) { + return this; + } + + @Override + public ResponseSpec retrieve() { + return null; + } + + @Override + public T exchange(final ExchangeFunction exchangeFunction, final boolean close) { + return null; + } + + @Override + public RequestBodySpec uri(final URI uri) { + return this; + } + + @Override + public RequestBodySpec uri(final String uri, final Object... uriVariables) { + return this; + } + + @Override + public RequestBodySpec uri(final String uri, final Map uriVariables) { + return this; + } + + @Override + public RequestBodySpec uri(final String uri, final Function uriFunction) { + return this; + } + + @Override + public RequestBodySpec uri(final Function uriFunction) { + return this; + } + } + +} diff --git a/backend/src/test/java/site/coduo/member/client/GithubApiClientTest.java b/backend/src/test/java/site/coduo/member/client/GithubApiClientTest.java new file mode 100644 index 000000000..5ad40b70b --- /dev/null +++ b/backend/src/test/java/site/coduo/member/client/GithubApiClientTest.java @@ -0,0 +1,45 @@ +package site.coduo.member.client; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import site.coduo.fake.FakeClient; +import site.coduo.member.client.dto.GithubUserRequest; +import site.coduo.member.client.dto.GithubUserResponse; +import site.coduo.member.exception.ExternalApiCallException; +import site.coduo.member.infrastructure.http.Bearer; + +class GithubApiClientTest { + + + @Test + @DisplayName("Github로부터 회원정보를 불러온다.") + void get_member_from_github() { + // given + final FakeClient client = new FakeClient(); + final GithubApiClient githubApiClient = new GithubApiClient(client); + final GithubUserRequest request = new GithubUserRequest(new Bearer("ok")); + + // when + final GithubUserResponse response = githubApiClient.getUser(request); + + // then + assertThat(response).isEqualTo(new GithubUserResponse("userId", "login", "avatar_url")); + } + + @Test + @DisplayName("토큰값이 잘못된 경우 예외 발생한다.") + void throw_exception_when_token_is_invalid() { + // given + final FakeClient client = new FakeClient(); + final GithubApiClient githubApiClient = new GithubApiClient(client); + final GithubUserRequest request = new GithubUserRequest(new Bearer("")); + + // when & then + assertThatThrownBy(() -> githubApiClient.getUser(request)) + .isInstanceOf(ExternalApiCallException.class); + } +} From 7e3d30d83ab4a0286656cf6a01f9c5549a2db6fd Mon Sep 17 00:00:00 2001 From: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:05:30 +0900 Subject: [PATCH 74/74] =?UTF-8?q?[BE]=20=ED=9A=8C=EC=9B=90=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=20API=20=EC=B6=94=EA=B0=80=20(#760)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 회원 탈퇴 기능 API 추가 * docs: 회원 탈퇴 API 문서 추가 * fix: 링크 삭제 테스트 id 하드코딩된 것 변경 * test: 사용하지 않는 메서드 삭제 * test: 회원 탈퇴 테스트 추가 * refactor: member를 조회 했을 시 없을 때 예외 처리 로직 service에서 repository로 이동 * refactor: 물리 삭제에서 논리 삭제로 변경 --- .../member/controller/MemberController.java | 13 +++- .../controller/docs/MemberControllerDocs.java | 10 +++ .../java/site/coduo/member/domain/Member.java | 19 +++++- .../domain/repository/MemberRepository.java | 18 ++++++ .../coduo/member/service/MemberService.java | 16 +++-- .../acceptance/MemberAcceptanceTest.java | 62 ++++++++++++++++--- .../member/service/MemberServiceTest.java | 26 ++++++++ 7 files changed, 148 insertions(+), 16 deletions(-) diff --git a/backend/src/main/java/site/coduo/member/controller/MemberController.java b/backend/src/main/java/site/coduo/member/controller/MemberController.java index e846b0432..e0cafb981 100644 --- a/backend/src/main/java/site/coduo/member/controller/MemberController.java +++ b/backend/src/main/java/site/coduo/member/controller/MemberController.java @@ -4,6 +4,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -19,11 +20,17 @@ public class MemberController implements MemberControllerDocs { private final MemberService memberService; @GetMapping("/member") - public ResponseEntity getMember( - @CookieValue(SIGN_IN_COOKIE_NAME) final String token - ) { + public ResponseEntity getMember(@CookieValue(SIGN_IN_COOKIE_NAME) final String token) { final MemberReadResponse response = memberService.findMemberNameByCredential(token); return ResponseEntity.ok(response); } + + @DeleteMapping("/member") + public ResponseEntity deleteMember(@CookieValue(SIGN_IN_COOKIE_NAME) final String token) { + memberService.deleteMember(token); + + return ResponseEntity.noContent() + .build(); + } } diff --git a/backend/src/main/java/site/coduo/member/controller/docs/MemberControllerDocs.java b/backend/src/main/java/site/coduo/member/controller/docs/MemberControllerDocs.java index ef0150df5..de75df8f8 100644 --- a/backend/src/main/java/site/coduo/member/controller/docs/MemberControllerDocs.java +++ b/backend/src/main/java/site/coduo/member/controller/docs/MemberControllerDocs.java @@ -29,4 +29,14 @@ ResponseEntity getMember( schema = @Schema(type = "string") ) String token); + + @ApiResponse(responseCode = "204", description = "회원 정보를 삭제한다.", + content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) + ResponseEntity deleteMember( + @Parameter( + in = ParameterIn.COOKIE, + name = "coduo_whoami", + description = "사용자가 인증에 성공하면 서버에서 발급하는 쿠키", + schema = @Schema(type = "string") + ) String token); } diff --git a/backend/src/main/java/site/coduo/member/domain/Member.java b/backend/src/main/java/site/coduo/member/domain/Member.java index fe3888c33..81bbb23a0 100644 --- a/backend/src/main/java/site/coduo/member/domain/Member.java +++ b/backend/src/main/java/site/coduo/member/domain/Member.java @@ -1,5 +1,6 @@ package site.coduo.member.domain; +import java.time.LocalDateTime; import java.util.Objects; import jakarta.persistence.Column; @@ -9,6 +10,8 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; +import org.springframework.format.annotation.DateTimeFormat; + import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -41,14 +44,19 @@ public class Member extends BaseTimeEntity { @Column(name = "USER_NAME") private String username; + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Column(name = "DELETED_AT") + private LocalDateTime deletedAt; + @Builder private Member(final String accessToken, final String loginId, final String userId, final String profileImage, - final String username) { + final String username, final LocalDateTime deletedAt) { this.accessToken = accessToken; this.loginId = loginId; this.userId = userId; this.profileImage = profileImage; this.username = username; + this.deletedAt = deletedAt; } public void update(final Member other) { @@ -57,6 +65,15 @@ public void update(final Member other) { this.userId = other.userId; this.profileImage = other.profileImage; this.username = other.username; + this.deletedAt = other.deletedAt; + } + + public void delete() { + this.deletedAt = LocalDateTime.now(); + } + + public boolean isDeleted() { + return deletedAt != null; } @Override diff --git a/backend/src/main/java/site/coduo/member/domain/repository/MemberRepository.java b/backend/src/main/java/site/coduo/member/domain/repository/MemberRepository.java index 0e1f72eaa..af500af57 100644 --- a/backend/src/main/java/site/coduo/member/domain/repository/MemberRepository.java +++ b/backend/src/main/java/site/coduo/member/domain/repository/MemberRepository.java @@ -1,15 +1,33 @@ package site.coduo.member.domain.repository; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import site.coduo.member.domain.Member; +import site.coduo.member.exception.MemberNotFoundException; public interface MemberRepository extends JpaRepository { Optional findByUserId(String userId); + List findByDeletedAtIsNull(); + + @Override + default List findAll() { + return findByDeletedAtIsNull(); + } + + default Member fetchByUserId(final String userId) { + final Member member = findByUserId(userId) + .orElseThrow(() -> new MemberNotFoundException(String.format("%s는 찾을 수 없는 회원 아이디입니다.", userId))); + if (member.isDeleted()) { + throw new MemberNotFoundException(String.format("%s는 삭제된 회원입니다.", userId)); + } + return member; + } + boolean existsByUserId(String userId); } diff --git a/backend/src/main/java/site/coduo/member/service/MemberService.java b/backend/src/main/java/site/coduo/member/service/MemberService.java index 1212f6482..f56b345b2 100644 --- a/backend/src/main/java/site/coduo/member/service/MemberService.java +++ b/backend/src/main/java/site/coduo/member/service/MemberService.java @@ -10,7 +10,6 @@ import site.coduo.member.client.dto.GithubUserResponse; import site.coduo.member.domain.Member; import site.coduo.member.domain.repository.MemberRepository; -import site.coduo.member.exception.MemberNotFoundException; import site.coduo.member.infrastructure.http.Bearer; import site.coduo.member.infrastructure.security.JwtProvider; import site.coduo.member.service.dto.member.MemberReadResponse; @@ -36,15 +35,22 @@ public void createMember(final String username, final String encryptedAccessToke public MemberReadResponse findMemberNameByCredential(final String token) { final String userId = jwtProvider.extractSubject(token); - final Member member = memberRepository.findByUserId(userId) - .orElseThrow(() -> new MemberNotFoundException(String.format("%s는 찾을 수 없는 회원 아이디입니다.", userId))); + final Member member = memberRepository.fetchByUserId(userId); return new MemberReadResponse(member.getUsername()); } public Member findMemberByCredential(final String token) { final String userId = jwtProvider.extractSubject(token); - return memberRepository.findByUserId(userId) - .orElseThrow(() -> new MemberNotFoundException(String.format("%s는 찾을 수 없는 회원 아이디입니다.", userId))); + + return memberRepository.fetchByUserId(userId); + } + + @Transactional + public void deleteMember(final String token) { + final String userId = jwtProvider.extractSubject(token); + final Member member = memberRepository.fetchByUserId(userId); + + member.delete(); } } diff --git a/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java b/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java index 254e92445..f8cffac08 100644 --- a/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java +++ b/backend/src/test/java/site/coduo/acceptance/MemberAcceptanceTest.java @@ -50,19 +50,67 @@ void search_member_info() { .body("username", is(member.getUsername())); } - String login(Member member) { - final String sessionId = GithubAcceptanceTest.createAccessTokenCookie(); + @Test + @DisplayName("회원을 삭제한다.") + void delete_member() { + //given + final Member member = Member.builder() + .userId("123") + .accessToken("access") + .loginId("login") + .username("username") + .profileImage("some image") + .build(); + final String loginToken = jwtProvider.sign(member.getUserId()); memberRepository.save(member); - return RestAssured + //when && then + RestAssured .given() - .cookie("JSESSIONID", sessionId) + .cookie(SIGN_IN_COOKIE_NAME, loginToken) .when() - .get("/api/sign-in/callback") + .delete("/api/member") - .thenReturn() - .cookie("coudo_whoami"); + .then() + .statusCode(HttpStatus.SC_NO_CONTENT); + } + + @Test + @DisplayName("존재하지 않는 회원을 삭제한다.") + void delete_not_member() { + //given + final Member member = Member.builder() + .userId("123") + .accessToken("access") + .loginId("login") + .username("username") + .profileImage("some image") + .build(); + + final String loginToken = jwtProvider.sign(member.getUserId()); + memberRepository.save(member); + + //when && then + RestAssured + .given() + .cookie(SIGN_IN_COOKIE_NAME, loginToken) + + .when() + .delete("/api/member") + + .then() + .statusCode(HttpStatus.SC_NO_CONTENT); + + RestAssured + .given() + .cookie(SIGN_IN_COOKIE_NAME, loginToken) + + .when() + .delete("/api/member") + + .then() + .statusCode(HttpStatus.SC_NOT_FOUND); } } diff --git a/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java b/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java index 4dddba76c..ef8eead9a 100644 --- a/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java +++ b/backend/src/test/java/site/coduo/member/service/MemberServiceTest.java @@ -2,6 +2,8 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.List; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -89,4 +91,28 @@ void search_member_by_login_token() { // then assertThat(findMember.getUsername()).isEqualTo(member.getUsername()); } + + @Test + @DisplayName("회원을 삭제한다.") + void delete_member() { + // given + final Member member = Member.builder() + .userId("userid") + .accessToken("access") + .loginId("login") + .username("username") + .profileImage("some image") + .build(); + final String token = jwtProvider.sign(member.getUserId()); + + memberRepository.save(member); + final List beforeDelete = memberRepository.findAll(); + + // when + memberService.deleteMember(token); + + //then + final List afterDelete = memberRepository.findAll(); + assertThat(afterDelete).hasSize(beforeDelete.size() - 1); + } }