Skip to content

Commit

Permalink
chore: (#703) dev 브런치와 충돌나는 코드 병합 및 button 글자 정렬 start로 속성 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
Gilpop8663 committed Oct 5, 2023
2 parents 5d4a21b + 4d0e843 commit 4444985
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public ReissuedTokenDto reissueAuthToken(
validateTokenInfo(accessTokenPayload, refreshTokenPayload);

final String newAccessToken = tokenProcessor.generateAccessToken(accessTokenPayload.memberId());
final String newRefreshToken = tokenProcessor.generateRefreshToken(accessTokenPayload.memberId());
final String newRefreshToken = tokenProcessor.generateRefreshToken(refreshTokenPayload.memberId());
redisTemplate.opsForValue().set(newRefreshToken, accessTokenPayload.memberId(), Duration.ofDays(14L));
return new ReissuedTokenDto(newAccessToken, newRefreshToken);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.votogether.domain.auth.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.SoftAssertions.assertSoftly;

import com.votogether.domain.auth.dto.request.AccessTokenRequest;
import com.votogether.domain.auth.service.dto.ReissuedTokenDto;
import com.votogether.domain.auth.service.oauth.KakaoOAuthClient;
import com.votogether.domain.member.service.MemberService;
import com.votogether.global.exception.BadRequestException;
import com.votogether.global.jwt.TokenProcessor;
import com.votogether.test.config.RedisTestConfig;
import java.util.Objects;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.core.RedisTemplate;

@Import(RedisTestConfig.class)
@SpringBootTest
class AuthServiceTest {

@Autowired
AuthService authService;

@Autowired
KakaoOAuthClient kakaoOAuthClient;

@Autowired
MemberService memberService;

@Autowired
TokenProcessor tokenProcessor;

@Autowired
RedisTemplate<String, Long> redisTemplate;

@Nested
@DisplayName("인증 토큰을 재발급 받을 때")
class ReissueAuthToken {

@AfterEach
void tearDown() {
Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection().serverCommands().flushAll();
}

@Test
@DisplayName("정상적으로 인증 토큰을 재발급한다.")
void success() {
// given
Long memberId = 1L;
String accessToken = tokenProcessor.generateAccessToken(memberId);
String refreshToken = tokenProcessor.generateRefreshToken(memberId);
AccessTokenRequest request = new AccessTokenRequest(accessToken);

redisTemplate.opsForValue().set(refreshToken, memberId);

// when
ReissuedTokenDto reissuedTokenDto = authService.reissueAuthToken(request, refreshToken);

// then
Long savedMemberId = redisTemplate.opsForValue().get(reissuedTokenDto.refreshToken());
Long dbSize = Objects.requireNonNull(redisTemplate.getConnectionFactory())
.getConnection()
.serverCommands()
.dbSize();

assertSoftly(softly -> {
softly.assertThat(memberId).isEqualTo(savedMemberId);
softly.assertThat(dbSize).isEqualTo(1L);
}
);
}

@Test
@DisplayName("인증 토큰과 갱신 토큰의 회원 아이디가 다르면 예외가 발생한다.")
void throwsExceptionWhenInformationOfAccessAndRefresh() {
// given
Long accessTokenMemberId = 1L;
Long refreshTokenMemberId = 2L;
String accessToken = tokenProcessor.generateAccessToken(accessTokenMemberId);
String refreshToken = tokenProcessor.generateRefreshToken(refreshTokenMemberId);
AccessTokenRequest request = new AccessTokenRequest(accessToken);

redisTemplate.opsForValue().set(refreshToken, refreshTokenMemberId);

// when, then
assertThatThrownBy(() -> authService.reissueAuthToken(request, refreshToken))
.isInstanceOf(BadRequestException.class)
.hasMessage("토큰 간의 정보가 일치하지 않습니다.");
}

}

@Nested
@DisplayName("갱신 토큰을 삭제할 때")
class DeleteRefreshToken {

@Test
@DisplayName("정상적으로 삭제한다.")
void deleteRefreshToken() {
// given
Long memberId = 3L;
String refreshToken = "ihg.fed.cba";
redisTemplate.opsForValue().set(refreshToken, memberId);

// when
authService.deleteRefreshToken(refreshToken);

// then
assertThat(redisTemplate.keys(refreshToken).size()).isZero();
}

}

}
2 changes: 2 additions & 0 deletions frontend/src/components/comment/CommentList/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export const HiddenInput = styled.input`
position: absolute;
bottom: 0;
outline: none;
color: white;
z-index: -1;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const IsDeletable = () => {
handleDeleteOptionClick={() => {}}
handleRemoveImageClick={() => {}}
handleUploadImage={() => {}}
handlePasteImage={() => {}}
optionId={Math.floor(Math.random() * 100000)}
text="방학 때 강릉으로 강아지와 기차여행을 하려했지
만 장마가 와서 취소했어요. 여행을 별로 좋"
Expand All @@ -42,6 +43,7 @@ export const IsNotDeletable = () => {
handleDeleteOptionClick={() => {}}
handleRemoveImageClick={() => {}}
handleUploadImage={() => {}}
handlePasteImage={() => {}}
optionId={Math.floor(Math.random() * 100000)}
text="방학 때 강릉으로 강아지와 기차여행을 하려했지
만 장마가 와서 취소했어요. 여행을 별로 좋"
Expand All @@ -62,6 +64,7 @@ export const ShowImage = () => {
handleDeleteOptionClick={() => {}}
handleRemoveImageClick={() => {}}
handleUploadImage={() => {}}
handlePasteImage={() => {}}
optionId={Math.floor(Math.random() * 100000)}
text="방학 때 강릉으로 강아지와 기차여행을 하려했지
만 장마가 와서 취소했어요. 여행을 별로 좋"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChangeEvent, MutableRefObject } from 'react';
import { ChangeEvent, ClipboardEvent, MutableRefObject } from 'react';

import { POST_WRITING_OPTION } from '@constants/policy';
import { POST_OPTION_POLICY } from '@constants/policyMessage';

import OptionCancelButton from './OptionCancelButton';
Expand All @@ -11,17 +12,16 @@ interface WritingVoteOptionProps {
text: string;
isDeletable: boolean;
ariaLabel: string;
handleUpdateOptionChange: (event: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
handleUpdateOptionChange: (event: ChangeEvent<HTMLTextAreaElement>) => void;
handleDeleteOptionClick: () => void;
handleRemoveImageClick: () => void;
handleUploadImage: (event: ChangeEvent<HTMLInputElement>) => void;
handlePasteImage: (event: ClipboardEvent<HTMLTextAreaElement>) => void;
imageUrl: string;
contentInputRefList: MutableRefObject<HTMLInputElement[]>;
index: number;
}

const MAX_WRITING_LENGTH = 50;

export default function WritingVoteOption({
optionId,
text,
Expand All @@ -31,6 +31,7 @@ export default function WritingVoteOption({
handleDeleteOptionClick,
handleRemoveImageClick,
handleUploadImage,
handlePasteImage,
imageUrl,
contentInputRefList,
index,
Expand All @@ -47,9 +48,10 @@ export default function WritingVoteOption({
<S.ContentTextArea
name="optionText"
defaultValue={text}
onChange={(e: ChangeEvent<HTMLTextAreaElement>) => handleUpdateOptionChange(e)}
onChange={handleUpdateOptionChange}
onPaste={handlePasteImage}
placeholder={POST_OPTION_POLICY.DEFAULT}
maxLength={MAX_WRITING_LENGTH}
maxLength={POST_WRITING_OPTION.MAX_LENGTH}
/>

<OptionUploadImageButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChangeEvent, MutableRefObject } from 'react';
import { ChangeEvent, ClipboardEvent, MutableRefObject } from 'react';

import { WritingVoteOptionType } from '@hooks/useWritingOption';

Expand All @@ -21,6 +21,7 @@ interface WritingVoteOptionListProps {
removeImage: (optionId: number) => void;
handleUploadImage: (event: ChangeEvent<HTMLInputElement>, optionId: number) => void;
contentInputRefList: MutableRefObject<HTMLInputElement[]>;
handlePasteImage: (event: ClipboardEvent<HTMLTextAreaElement>, optionId: number) => void;
};
}

Expand All @@ -33,6 +34,7 @@ export default function WritingVoteOptionList({ writingOptionHook }: WritingVote
removeImage,
handleUploadImage,
contentInputRefList,
handlePasteImage,
} = writingOptionHook;
const isDeletable = optionList.length > MINIMUM_COUNT;

Expand All @@ -53,6 +55,9 @@ export default function WritingVoteOptionList({ writingOptionHook }: WritingVote
}
imageUrl={optionItem.imageUrl}
contentInputRefList={contentInputRefList}
handlePasteImage={(event: ClipboardEvent<HTMLTextAreaElement>) =>
handlePasteImage(event, optionItem.id)
}
index={index}
/>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface ProgressBarProps {
export default function ProgressBar({ percent, isSelected }: ProgressBarProps) {
return (
<S.Container>
<S.Bar progress={percent} $isSelected={isSelected} />
<S.Bar $progress={percent} $isSelected={isSelected} />
</S.Container>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export const Container = styled.div`
background-color: rgba(0, 0, 0, 0.15);
`;

export const Bar = styled.div<{ progress: number; $isSelected: boolean }>`
export const Bar = styled.div<{ $progress: number; $isSelected: boolean }>`
border-radius: 4px;
width: ${({ progress }) => `${progress}%`};
width: ${({ $progress }) => `${$progress}%`};
height: 8px;
background-color: ${({ $isSelected }) => ($isSelected ? 'var(--primary-color)' : '#9F9F9F')};
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/components/post/Post/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useContext, useEffect } from 'react';
import { ForwardedRef, forwardRef, memo, useContext, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

import { PostInfo } from '@type/post';
Expand Down Expand Up @@ -30,7 +30,10 @@ interface PostProps {
isPreview: boolean;
}

export default memo(function Post({ postInfo, isPreview }: PostProps) {
const Post = forwardRef(function Post(
{ postInfo, isPreview }: PostProps,
ref: ForwardedRef<HTMLLIElement>
) {
const navigate = useNavigate();
const {
postId,
Expand Down Expand Up @@ -112,7 +115,7 @@ export default memo(function Post({ postInfo, isPreview }: PostProps) {
const isPreviewTabIndex = isPreview ? undefined : 0;

return (
<S.Container as={isPreview ? 'li' : 'div'}>
<S.Container as={isPreview ? 'li' : 'div'} ref={ref} $isPreview={isPreview}>
<S.DetailLink
role={isPreview ? 'link' : 'none'}
tabIndex={0}
Expand Down Expand Up @@ -203,3 +206,5 @@ export default memo(function Post({ postInfo, isPreview }: PostProps) {
</S.Container>
);
});

export default memo(Post);
6 changes: 5 additions & 1 deletion frontend/src/components/post/Post/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { styled } from 'styled-components';

import { theme } from '@styles/theme';

export const Container = styled.li`
export const Container = styled.li<{ $isPreview: boolean }>`
width: 100%;
position: relative;
Expand All @@ -11,6 +11,9 @@ export const Container = styled.li`
letter-spacing: 0.5px;
line-height: 1.5;
padding-bottom: ${({ $isPreview }) => $isPreview && '30px'};
border-bottom: ${({ $isPreview }) => $isPreview && '1px solid rgba(0, 0, 0, 0.1)'};
@media (min-width: ${theme.breakpoint.sm}) {
font: var(--text-caption);
}
Expand Down Expand Up @@ -78,6 +81,7 @@ export const Content = styled.div<{ $isPreview: boolean }>`
text-overflow: ellipsis;
word-break: break-word;
white-space: pre-wrap;
text-align: start;
overflow: hidden;
Expand Down
20 changes: 6 additions & 14 deletions frontend/src/components/post/PostList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function PostList() {

return (
<S.Container>
<button ref={topButtonRef} role="contentinfo" aria-label="최상단입니다"></button>
<button ref={topButtonRef} role="contentinfo" aria-label="최상단입니다" />
<S.SelectContainer>
<S.SelectWrapper>
<Select<PostStatus>
Expand Down Expand Up @@ -102,23 +102,15 @@ export default function PostList() {
<React.Fragment key={pageIndex}>
{postListInfo.postList.map((post, index) => {
if (index === 7) {
return (
<div key={post.postId} ref={targetRef}>
<Post isPreview={true} postInfo={post} />
</div>
);
return <Post key={post.postId} ref={targetRef} isPreview={true} postInfo={post} />;
}

return <Post key={post.postId} isPreview={true} postInfo={post} />;
})}
<S.HiddenButton
onClick={focusTopContent}
aria-label="스크롤 맨 위로가기"
></S.HiddenButton>
<S.HiddenLink
aria-label="게시글 작성 페이지로 이동"
to={PATH.POST_WRITE}
></S.HiddenLink>
<li key={`${pageIndex}UserButton`}>
<S.HiddenButton onClick={focusTopContent} aria-label="스크롤 맨 위로가기" />
<S.HiddenLink aria-label="게시글 작성 페이지로 이동" to={PATH.POST_WRITE} />
</li>
</React.Fragment>
))}
{isFetchingNextPage && <Skeleton isLarge={false} />}
Expand Down
10 changes: 0 additions & 10 deletions frontend/src/components/post/PostList/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,6 @@ export const PostListContainer = styled.ul`
gap: 30px;
padding: 30px 20px;
> div > li {
padding-bottom: 30px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
> li {
padding-bottom: 30px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
`;

export const SelectWrapper = styled.div`
Expand Down
Loading

0 comments on commit 4444985

Please sign in to comment.