Skip to content

Commit

Permalink
Release 1.1.1 출시 (#615)
Browse files Browse the repository at this point in the history

* fix: (#598) 로그인 시 쿠키를 주고 받을 수 있도록 fetch 설정 변경 (#600)

* cookie의 path를 /auth에서 /로 변경 및 RefreshToken TTL설정 (#610)

* refactor: (#608) cookie의 path를 루트 경로로 지정

* refactor: (#608) refreshtoken을 redis에 저장할 때 ttl설정

* 구글 애널리틱스 작동을 위한 모듈 설치, 게시글 본문의 링크 클릭 가능하도록 구현 (#613)

* fix: (#612) react-gtm-module을 react-ga4 로 대체, RouteChangeTracker 컴포넌트 추가

* feat: (#606) 게시글 작성 시 링크 넣기 기능 추가

* feat: (#606) 게시글 본문에 링크 있으면 a 태그로 인식되도록 구현

* chore: (#606) 공지사항 설명 수정

* chore: (#606) 불필요한 주석 삭제

* 로그인, 투표 통계, 회원정보 페이지 lazy import 적용 (#571)

* feat: (#556) lazy import 설정 및 트리쉐이킹 설정

* feat: (#556) 로그인, 투표 통계, 회원정보 입력 페이지 lazy import 적용

* feat: (#556) 번들 이름 매번 바뀌도록 변경 및 수정되었던 코드 복구

* feat: (#556) Suspense 코드 복구

---------

Co-authored-by: jero_kang <[email protected]>

* 게시글 작성 시 이미지 파일을 보낼 때 webp 로 압축하여 성능 개선 (#614)

* feat: (#555) browser-image-compression 설치 및 본문 이미지 훅에 적용

* feat: (#555) 선택지 옵션 사진을 webp로 변환하도록 구현

---------

Co-authored-by: 최우창 <[email protected]>
Co-authored-by: Jun-Hyeok Sin <[email protected]>
Co-authored-by: jero_kang <[email protected]>
Co-authored-by: jeomxon <[email protected]>
Co-authored-by: aiaiaiai1 <[email protected]>
Co-authored-by: chsua <[email protected]>
Co-authored-by: lookh <[email protected]>
Co-authored-by: 김영길/KIM YOUNG GIL <[email protected]>
Co-authored-by: jero_kang <[email protected]>
Co-authored-by: chsua <[email protected]>
  • Loading branch information
11 people authored Sep 14, 2023
1 parent 8f8184e commit 5c72bc8
Show file tree
Hide file tree
Showing 22 changed files with 240 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private void addRefreshTokenToCookie(final HttpServletResponse httpServletRespon
final ResponseCookie responseCookie = ResponseCookie.from("refreshToken", refreshToken)
.httpOnly(true)
.secure(true)
.path("/auth")
.path("/")
.maxAge(1209600)
.sameSite(SameSite.NONE.attributeValue())
.build();
Expand All @@ -94,7 +94,7 @@ private void expireCookie(final HttpServletResponse httpServletResponse, final S
final ResponseCookie responseCookie = ResponseCookie.from("refreshToken", refreshToken)
.httpOnly(true)
.secure(true)
.path("/auth")
.path("/")
.maxAge(0)
.sameSite(SameSite.NONE.attributeValue())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.votogether.global.jwt.TokenPayload;
import com.votogether.global.jwt.TokenProcessor;
import com.votogether.global.jwt.exception.JsonException;
import java.time.Duration;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
Expand Down Expand Up @@ -58,7 +59,7 @@ public ReissuedTokenDto reissueAuthToken(

final String newAccessToken = tokenProcessor.generateAccessToken(accessTokenPayload.memberId());
final String newRefreshToken = tokenProcessor.generateRefreshToken(accessTokenPayload.memberId());
redisTemplate.opsForValue().set(newRefreshToken, accessTokenPayload.memberId());
redisTemplate.opsForValue().set(newRefreshToken, accessTokenPayload.memberId(), Duration.ofDays(14L));
return new ReissuedTokenDto(newAccessToken, newRefreshToken);
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ declare module NodeJS {
VOTOGETHER_REST_API_KEY: string;
VOTOGETHER_SERVER_REDIRECT_URL: string;
VOTOGETHER_CHANNEL_TALK_KEY: string;
VOTOGETHER_GOOGLE_TAG_ID: string;
VOTOGETHER_GOOGLE_ANALYTICS_ID: string;
}
}
18 changes: 9 additions & 9 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@
},
"dependencies": {
"@tanstack/react-query": "^4.29.19",
"browser-image-compression": "^2.0.2",
"dotenv": "^16.3.1",
"msw": "^1.2.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-gtm-module": "^2.0.11",
"react-ga4": "^2.1.0",
"react-router-dom": "^6.14.1"
},
"eslintConfig": {
Expand Down
6 changes: 3 additions & 3 deletions frontend/public/seo/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
>
<url>
<loc>https://votogether.com/</loc>
<lastmod>2023-09-13T04:22:25.347Z</lastmod>
<lastmod>2023-09-14T06:06:57.224Z</lastmod>
</url>

<url>
<loc>https://votogether.com/login</loc>
<lastmod>2023-09-13T04:22:25.347Z</lastmod>
<lastmod>2023-09-14T06:06:57.224Z</lastmod>
</url>

<url>
<loc>https://votogether.com/ranking</loc>
<lastmod>2023-09-13T04:22:25.347Z</lastmod>
<lastmod>2023-09-14T06:06:57.224Z</lastmod>
</url>
</urlset>
30 changes: 15 additions & 15 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Suspense } from 'react';
import { RouterProvider } from 'react-router-dom';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
Expand All @@ -11,7 +12,7 @@ import router from '@routes/router';
import ErrorBoundaryForTopClass from '@pages/ErrorBoundaryForTopClass';

import ChannelTalk from '@components/ChannelTalk';
import GoogleTagManager from '@components/GoogleTagManager';
import Skeleton from '@components/common/Skeleton';

import { GlobalStyle } from '@styles/globalStyle';
import { theme } from '@styles/theme';
Expand All @@ -24,21 +25,20 @@ ChannelTalk.boot({
});

const App = () => (
<>
<ErrorBoundaryForTopClass>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<GlobalStyle />
<PostOptionProvider>
<AuthProvider>
<ErrorBoundaryForTopClass>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<GlobalStyle />
<PostOptionProvider>
<AuthProvider>
<Suspense fallback={<Skeleton isLarge />}>
<RouterProvider router={router} />
</AuthProvider>
</PostOptionProvider>
</ThemeProvider>
</QueryClientProvider>
</ErrorBoundaryForTopClass>
<GoogleTagManager gtmId={process.env.VOTOGETHER_GOOGLE_TAG_ID} />
</>
</Suspense>
</AuthProvider>
</PostOptionProvider>
</ThemeProvider>
</QueryClientProvider>
</ErrorBoundaryForTopClass>
);

export default App;
10 changes: 0 additions & 10 deletions frontend/src/components/GoogleTagManager/index.tsx

This file was deleted.

15 changes: 14 additions & 1 deletion frontend/src/components/PostForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ export default function PostForm({ data, mutate }: PostFormProps) {
};

const { text: writingTitle, handleTextChange: handleTitleChange } = useText(title ?? '');
const { text: writingContent, handleTextChange: handleContentChange } = useText(content ?? '');
const {
text: writingContent,
handleTextChange: handleContentChange,
addText: addContent,
} = useText(content ?? '');
const multiSelectHook = useMultiSelect(categoryIds ?? [], CATEGORY_COUNT_LIMIT);

const handleDeadlineButtonClick = (option: DeadlineOption) => {
Expand All @@ -121,6 +125,10 @@ export default function PostForm({ data, mutate }: PostFormProps) {
}
};

const handleInsertContentLink = () => {
addContent('[[이 괄호 안에 링크를 작성해주세요]] ');
};

const handlePostFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData();
Expand Down Expand Up @@ -227,6 +235,11 @@ export default function PostForm({ data, mutate }: PostFormProps) {
minLength={POST_CONTENT.MIN_LENGTH}
required
/>
<S.ContentLinkButtonWrapper>
<S.Button onClick={handleInsertContentLink} type="button">
본문에 링크 넣기
</S.Button>
</S.ContentLinkButtonWrapper>
<S.ContentImagePartWrapper $hasImage={!!contentImageHook.contentImage}>
<ContentImagePart size="lg" contentImageHook={contentImageHook} />
</S.ContentImagePartWrapper>
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/components/PostForm/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,33 @@ export const Content = styled.textarea`
}
`;

export const ContentLinkButtonWrapper = styled.div`
width: 100%;
height: 36px;
margin-bottom: 5px;
@media (max-width: ${theme.breakpoint.sm}) {
}
`;

export const Button = styled.button`
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
border: 2px solid var(--primary-color);
border-radius: 5px;
padding: 5px 0;
color: var(--primary-color);
background-color: white;
font-size: 16px;
cursor: pointer;
`;

export const ContentImagePartWrapper = styled.div<{ $hasImage: boolean }>`
justify-self: ${props => props.$hasImage && 'center'};
height: 100%;
Expand Down
25 changes: 25 additions & 0 deletions frontend/src/components/RouteChangeTracker/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* src/RouteChangeTracker.js */
import { useEffect, useState } from 'react';
import ReactGA from 'react-ga4';
import { useLocation } from 'react-router-dom';

export default function RouteChangeTracker() {
const location = useLocation();
const [initialized, setInitialized] = useState(false);

useEffect(() => {
if (process.env.VOTOGETHER_GOOGLE_ANALYTICS_ID) {
ReactGA.initialize(process.env.VOTOGETHER_GOOGLE_ANALYTICS_ID);
setInitialized(true);
}
}, []);

useEffect(() => {
if (initialized) {
ReactGA.set({ page: location.pathname });
ReactGA.send('pageview');
}
}, [initialized, location]);

return <></>;
}
10 changes: 3 additions & 7 deletions frontend/src/components/common/Post/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MouseEvent, useContext, useEffect } from 'react';
import { useContext, useEffect } from 'react';

import { PostInfo } from '@type/post';

Expand All @@ -13,6 +13,7 @@ import { PATH } from '@constants/path';
import { POST } from '@constants/vote';

import { convertImageUrlToServerUrl } from '@utils/post/convertImageUrlToServerUrl';
import { linkifyText } from '@utils/post/formatContentLink';
import { checkClosedPost, convertTimeToWord } from '@utils/time';

import photoIcon from '@assets/photo_white.svg';
Expand Down Expand Up @@ -77,10 +78,6 @@ export default function Post({ postInfo, isPreview }: PostProps) {
});
};

const handleLinkClick = (e: MouseEvent<HTMLAnchorElement>) => {
if (!isPreview) e.preventDefault();
};

useEffect(() => {
if (isCreateError && createError instanceof Error) {
openToast(createError.message);
Expand All @@ -107,7 +104,6 @@ export default function Post({ postInfo, isPreview }: PostProps) {
as={isPreview ? '' : 'main'}
to={isPreview ? `${PATH.POST}/${postId}` : '#'}
$isPreview={isPreview}
onClick={handleLinkClick}
aria-describedby={
isPreview
? '해당 게시물의 상세페이지로 이동하기'
Expand Down Expand Up @@ -162,7 +158,7 @@ export default function Post({ postInfo, isPreview }: PostProps) {
aria-label={`내용: ${content}`}
$isPreview={isPreview}
>
{content}
{linkifyText(content)}
</S.Content>
{!isPreview && imageUrl && (
<S.Image src={convertImageUrlToServerUrl(imageUrl)} alt={'본문에 포함된 이미지'} />
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/components/common/Post/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export const Wrapper = styled.div`
}
`;

export const Content = styled.p<{ $isPreview: boolean }>`
export const Content = styled.div<{ $isPreview: boolean }>`
display: -webkit-box;
margin: 10px 0;
Expand All @@ -116,8 +116,6 @@ export const DetailLink = styled(Link)<{ $isPreview: boolean }>`
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: ${({ $isPreview }) => !$isPreview && 'none'};
`;

export const Image = styled.img`
Expand Down
18 changes: 13 additions & 5 deletions frontend/src/hooks/useContentImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { ChangeEvent, useRef, useState } from 'react';

import { MAX_FILE_SIZE } from '@components/PostForm/constants';

import { convertImageToWebP } from '@utils/resizeImage';

export const useContentImage = (imageUrl: string = '') => {
const [contentImage, setContentImage] = useState(imageUrl);
const contentInputRef = useRef<HTMLInputElement | null>(null);
Expand All @@ -11,13 +13,23 @@ export const useContentImage = (imageUrl: string = '') => {
if (contentInputRef.current) contentInputRef.current.value = '';
};

const handleUploadImage = (event: ChangeEvent<HTMLInputElement>) => {
const handleUploadImage = async (event: ChangeEvent<HTMLInputElement>) => {
const { files } = event.target;

if (!files) return;

const file = files[0];

const webpFileList = await convertImageToWebP(file);

event.target.files = webpFileList;

const reader = new FileReader();

const webpFile = webpFileList[0];

reader.readAsDataURL(webpFile);

event.target.setCustomValidity('');

if (file.size > MAX_FILE_SIZE) {
Expand All @@ -27,10 +39,6 @@ export const useContentImage = (imageUrl: string = '') => {
return;
}

const reader = new FileReader();

reader.readAsDataURL(file);

reader.onloadend = () => {
setContentImage(reader.result?.toString() ?? '');
};
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/hooks/useText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@ export const useText = (originalText: string) => {
setText('');
};

return { text, setText, handleTextChange, resetText };
const addText = (newTextToAdd: string) => {
setText(text + newTextToAdd);
};

return { text, setText, handleTextChange, resetText, addText };
};
Loading

0 comments on commit 5c72bc8

Please sign in to comment.