Skip to content

Commit

Permalink
Merge pull request #159 from Kernel360/develop
Browse files Browse the repository at this point in the history
Develop to Main merge
  • Loading branch information
kkkapuq authored Nov 21, 2023
2 parents e4fd080 + 59fe7c9 commit 4b229dc
Show file tree
Hide file tree
Showing 15 changed files with 601 additions and 224 deletions.
99 changes: 98 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,100 @@
# Orury!

당신만의 클라이밍 커뮤니티, 오루리
당신만의 클라이밍 커뮤니티, 오루리


## 멤버
|Backend| Backend |Backend|Backend|
|:---:|:-----------------------------------------------------------------------------------------------------------------------------:|:---:|:---:|
|<img src="https://github.com/Kernel360-4cell/algorithm-study/assets/44130863/eacb9aab-4a9b-4447-b516-9c5efe4484ce" width=100>| <img src="https://github.com/Kernel360/E2E1-Orury/assets/44130863/6fb7dacf-c5b6-4023-840f-96912da62910" width=100> |<img src="https://github.com/Kernel360-4cell/algorithm-study/assets/44130863/1ee6dd72-c060-4dab-996b-e9f9bc7048d2" width=100>|<img src="https://github.com/Kernel360-4cell/algorithm-study/assets/44130863/bef79d6c-5ec0-43c0-999c-906d42ad1e06" width=100>|
|[형준](https://github.com/kkkapuq)| [찬욱](https://github.com/mooncw) |[종민](https://github.com/ShineCorine)|[무룡](https://github.com/aqrms)|

## 기술 스택
<img src="https://github.com/Kernel360/E2E1-Orury/assets/44130863/48959e1d-06d5-4b0a-be68-6d83ff143040" width=400, height=800>

## 서비스 흐름도
![image](https://github.com/Kernel360/E2E1-Orury/assets/44130863/ef0c31d3-623a-4ef8-b9a8-2a8fe0e5706b)

## CI/CD
![image](https://github.com/Kernel360/E2E1-Orury/assets/44130863/23132200-f87c-4137-ad4b-6227a6174caa)

## 사용법
### 1. apk를 다운받아서 사용하는 경우
제공해드린 APK 파일을 다운받아 여러분의 휴대폰에 설치하고 사용해주세요

### 2. IntelliJ에서 플러터를 직접 실행해야 하는 경우
intellij 실행 > plugins 에서 Dart와 Flutter를 설치합니다.

1. Android SDK 설치
2. Flutter SDK 설치

requirement

M1 Macbook의 경우 Rosetta2가 설치되어 있어야 합니다

Xcode가 설치되어 있어야 합니다.

[https://docs.flutter.dev/get-started/install](https://docs.flutter.dev/get-started/install) 에서 운영체제에 맞는 flutter 설치법을 확인하실 수 있습니다

1. `sudo softwareupdate --install-rosetta --agree-to-license`
2. SDK를 설치합니다
1. 적절한 SDK를 다운받습니다.
1. [intel mac 버전](https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.16.0-stable.zip)
2. [Apple Silicon mac 버전](https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_arm64_3.16.0-stable.zip)
2. 적당한 곳에 압축을 풀고(압축을 푼 곳 주소가 필요합니다)

압축파일이 있는 곳으로 이동합니다 : `cd ~/development`
압축을 풉니다: `unzip ~/Downloads/flutter_macos_arm64_3.16.0-stable.zip`

3. 환경변수에 플러터 경로를 등록해줍니다

`export PATH**=**"$PATH:`pwd`/flutter/bin"`

만에 하나 계속 사용하실거면 .zshrc나 .bash_profile에 경로를 등록해야 합니다


3. 플러터 닥터를 실행해서 부족한 부분을 설정해줘야 합니다

`flutter doctor`

## 백엔드를 로컬에서 실행하는 방법

백엔드 프로젝트의 경로는 E2E1-Orury/backend/orury 입니다

1. 인텔리제이에서 해당 경로를 열어줍니다
2. backend/orury/src/main/java/com/kernel360/orury/config/JasyptConfig.java 파일에서
@Configuration 어노테이션을 주석처리 합니다
3. backend/orury/src/main/resources/application.yml 파일을 다음과 같이 수정합니다


```
..
url: ${LOCAL_DB_URL} # configuration에서 설정 필요
# url: ENC(4t9k8jNC5SIDd1hpsqRJiw5NgwtRAZ9l2cY42PcN3v6GNDC61KXk4X3nYwwUdW4+aC6g41zh14HyfWg+2fPmfuTXHg7aw+vTvL9Oomq1wEKM2r2ZHJbhmakdpmbeMVjVALC/MSk28Tp4a5sBhUFRWdpaFO2qQcpdUHUd9KsuJh0=)
username: ${LOCAL_DB_USERNAME}
# username: ENC(2STroDy2TfWobkrTroFwbRcxLke05156ZFMrqwKmVssNcN4foSX4I6ax8YAgdW3R)
password: ${LOCAL_DB_USER_PASSWORD}
# password: ENC(a8l7l/AYNz2x1HPYyu+urOBwVcBhQkeWgTi6Fvt+lQFq1QyHsL8yyi5+wr7lh26P)
jwt:
header: Authorization
#HS512 알고리즘을 사용할 것이기 때문에 512bit, 즉 64byte 이상의 secret key를 사용해야 한다.
#echo 'silvernine-tech-spring-boot-jwt-tutorial-secret-silvernine-tech-spring-boot-jwt-tutorial-secret'|base64
secret: ${LOCAL_JWT_SECRET_KEY}
# secret: ENC(XNkGVBeK1sJZhLhri+zz7pJOhHCvfif265mvT8OUIbOGeQcOCtHNnG2s3qjsKNe2u+dLoNVQBzbF1bKUfDxi8Po5tL7jQbZMPA33Dg1QMQFQWV46IyrYnLykYXQQvpin/SNPXW04ECDoRLF3TNwcS22D8uWEwe8L2wtcauyHeO1z+J6lUQArPHy76O2pzC7FHlBjOTw3STd23e3dd1WBQtHAYVmOIvNuPreulzSaHXc=)
access-validity: 1800
refresh-validity: 3600
```
시스템 환경변수나 IntelliJ Configuration에서
로컬 mysql 데이터베이스에 다음과 같은 환경 변수와 값을 등록해줍니다.
`LOCAL_DB_URL`, `LOCAL_DB_USERNAME`, `LOCAL_DB_USER_PASSWORD`, `LOCAL_JWT_SECRET_KEY`
또는 직접 등록해줘야 합니다.
토큰 만료시간을 조절하기 위해 `access-validity`, `refresh-validity`의 값을 적절히 입력해줍니다.(단위는 초 입니다)
Api 확인을 위해서 스웨거에 접속하거나 postman 등을 써서 테스트해주시면 됩니다
기본 포트는 8080 이고 swagger의 주소는 [다음](http://localhost:8080/swagger-ui/index.html)과 같습니다.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.mvcMatchers(SWAGGER.toArray(new String[0])).permitAll()
.antMatchers("/assets/**").permitAll()
.antMatchers("/admin/**").permitAll()
.antMatchers("/api/hello", "/api/auth/login", "/api/user/signup").permitAll()
.antMatchers("/api/hello", "/api/auth/login", "/api/user/signup", "/api/board/notice").permitAll()
.anyRequest().authenticated()
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import com.kernel360.orury.domain.board.model.BoardRequest;
import com.kernel360.orury.domain.board.service.BoardService;

import com.kernel360.orury.domain.post.model.PostDto;
import lombok.RequiredArgsConstructor;

import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

Expand Down Expand Up @@ -49,10 +51,17 @@ public BoardDto updateBoard(

// 특정 게시판 조회
@GetMapping("/{id}")
public BoardDto getBoard(
public ResponseEntity<BoardDto> getBoard(
@PathVariable Long id
) {
return boardService.getBoard(id);
return ResponseEntity.ok(boardService.getBoard(id));
}

// 공지 간단 조회
@GetMapping("/notice")
public ResponseEntity<List<PostDto>> getNoticeBoard(
) {
return ResponseEntity.ok(boardService.getNoticeBoard(1L));
}

// 게시판 삭제
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import com.kernel360.orury.domain.board.db.BoardRepository;
import com.kernel360.orury.domain.board.model.BoardDto;
import com.kernel360.orury.domain.board.model.BoardRequest;
import com.kernel360.orury.domain.post.db.PostEntity;
import com.kernel360.orury.domain.post.db.PostRepository;
import com.kernel360.orury.domain.post.model.PostDto;
import com.kernel360.orury.domain.post.service.PostConverter;
import com.kernel360.orury.global.constants.Constant;
import com.kernel360.orury.global.message.errors.ErrorMessages;
import lombok.RequiredArgsConstructor;
Expand All @@ -18,6 +22,8 @@ public class BoardService {

public final BoardRepository boardRepository;
public final BoardConverter boardConverter;
public final PostRepository postRepository;
public final PostConverter postConverter;

public BoardDto createBoard(
BoardRequest boardRequest
Expand Down Expand Up @@ -70,4 +76,15 @@ public BoardDto getBoard(Long id) {

return boardConverter.toDto(entity);
}

public List<PostDto> getNoticeBoard(Long id) {
var noticeList = postRepository.findAllByBoardId(id);
if(noticeList.isEmpty()) {
new RuntimeException(ErrorMessages.THERE_IS_NO_BOARD.getMessage() + id);
}

return noticeList.stream()
.map(postConverter::toNoticeDto) // 각 엔티티를 Dto로 변환
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface PostRepository extends JpaRepository<PostEntity, Long> {

// Optional<List<PostEntity>> findAllByBoardId(Long boardId);
List<PostEntity> findAllByBoardId(Long boardId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.kernel360.orury.domain.board.db.BoardEntity;
import com.kernel360.orury.domain.board.db.BoardRepository;
import com.kernel360.orury.domain.board.model.BoardDto;
import com.kernel360.orury.domain.comment.model.CommentDto;
import com.kernel360.orury.domain.comment.model.CommentLikeDto;
import com.kernel360.orury.domain.comment.service.CommentConverter;
Expand Down Expand Up @@ -35,9 +36,9 @@ public class PostConverter {
public PostDto toDto(PostEntity postEntity) {

var commentList = postEntity.getCommentList()
.stream()
.map(commentConverter::toDto)
.toList();
.stream()
.map(commentConverter::toDto)
.toList();

// map으로 변환
Map<String, List<CommentDto>> commentMap = new HashMap<>();
Expand All @@ -53,68 +54,68 @@ public PostDto toDto(PostEntity postEntity) {

// 게시글 작성자 userNickname 설정을 위한 entity
UserEntity userEntity = userRepository.findById(postEntity.getUserId())
.orElseThrow(() -> new RuntimeException(ErrorMessages.THERE_IS_NO_USER.getMessage() + postEntity.getUserId()));
.orElseThrow(() -> new RuntimeException(ErrorMessages.THERE_IS_NO_USER.getMessage() + postEntity.getUserId()));

// 좋아요 수 및 유저 좋아요 세팅
long loginId = getLoginId();
boolean isLike = getPostLike(loginId, postEntity.getId());
Long likeCnt = postLikeRepository.countByPostLikePKPostId(postEntity.getId());

return PostDto.builder()
.id(postEntity.getId())
.boardId(postEntity.getBoard().getId())
.postTitle(postEntity.getPostTitle())
.postContent(postEntity.getPostContent())
.userNickname(userEntity.getNickname())
.viewCnt(postEntity.getViewCnt())
.likeCnt(postEntity.getLikeCnt())
.userId(postEntity.getUserId())
.thumbnailUrl(postEntity.getThumbnailUrl())
.imageList(postEntity.getImages() == null ? List.of() : Arrays.stream(postEntity.getImages().split(","))
.map(image -> getenv().get("IMGUR_URL") + image)
.collect(Collectors.toList()))
.commentList(commentList)
.commentMap(commentMap)
.isLike(isLike)
.likeCnt(likeCnt.intValue())
.createdBy(postEntity.getCreatedBy())
.createdAt(postEntity.getCreatedAt())
.updatedBy(postEntity.getUpdatedBy())
.updatedAt(postEntity.getUpdatedAt())
.build();
.id(postEntity.getId())
.boardId(postEntity.getBoard().getId())
.postTitle(postEntity.getPostTitle())
.postContent(postEntity.getPostContent())
.userNickname(userEntity.getNickname())
.viewCnt(postEntity.getViewCnt())
.likeCnt(postEntity.getLikeCnt())
.userId(postEntity.getUserId())
.thumbnailUrl(postEntity.getThumbnailUrl())
.imageList(postEntity.getImages() == null ? List.of() : Arrays.stream(postEntity.getImages().split(","))
.map(image -> getenv().get("IMGUR_URL") + image)
.collect(Collectors.toList()))
.commentList(commentList)
.commentMap(commentMap)
.isLike(isLike)
.likeCnt(likeCnt.intValue())
.createdBy(postEntity.getCreatedBy())
.createdAt(postEntity.getCreatedAt())
.updatedBy(postEntity.getUpdatedBy())
.updatedAt(postEntity.getUpdatedAt())
.build();
}

public PostEntity toEntity(PostDto postDto) {
BoardEntity boardEntity = boardRepository.findById(postDto.getBoardId())
.orElseThrow(
() -> new RuntimeException(ErrorMessages.THERE_IS_NO_BOARD.getMessage() + postDto.getBoardId())
);
.orElseThrow(
() -> new RuntimeException(ErrorMessages.THERE_IS_NO_BOARD.getMessage() + postDto.getBoardId())
);

return PostEntity.builder()
.id(postDto.getId())
.board(boardEntity)
.postTitle(postDto.getPostTitle())
.postContent(postDto.getPostContent())
.viewCnt(postDto.getViewCnt())
.likeCnt(postDto.getLikeCnt())
.userId(postDto.getUserId())
.thumbnailUrl(postDto.getThumbnailUrl())
.images(postDto.getImageList().equals(List.of()) ? null : postDto.getImageList()
.stream()
.map(s -> s.replaceFirst(getenv().get("IMGUR_URL"), ""))
.collect(Collectors.joining(",")))
.createdBy(postDto.getCreatedBy())
.createdAt(postDto.getCreatedAt())
.updatedBy(postDto.getUpdatedBy())
.updatedAt(postDto.getUpdatedAt())
.build();
.id(postDto.getId())
.board(boardEntity)
.postTitle(postDto.getPostTitle())
.postContent(postDto.getPostContent())
.viewCnt(postDto.getViewCnt())
.likeCnt(postDto.getLikeCnt())
.userId(postDto.getUserId())
.thumbnailUrl(postDto.getThumbnailUrl())
.images(postDto.getImageList().equals(List.of()) ? null : postDto.getImageList()
.stream()
.map(s -> s.replaceFirst(getenv().get("IMGUR_URL"), ""))
.collect(Collectors.joining(",")))
.createdBy(postDto.getCreatedBy())
.createdAt(postDto.getCreatedAt())
.updatedBy(postDto.getUpdatedBy())
.updatedAt(postDto.getUpdatedAt())
.build();
}

public PostLikeDto toLikeDto(PostLikeEntity postLikeEntity) {
return PostLikeDto.builder()
.postId(postLikeEntity.getPostLikePK().getPostId())
.userId(postLikeEntity.getPostLikePK().getUserId())
.build();
.postId(postLikeEntity.getPostLikePK().getPostId())
.userId(postLikeEntity.getPostLikePK().getUserId())
.build();
}

// 게시글 좋아요 여부값 가져오기
Expand All @@ -128,7 +129,17 @@ private long getLoginId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String loginEmail = authentication.getName();
UserEntity loginUser = userRepository.findByEmailAddr(loginEmail)
.orElseThrow(() -> new NoSuchElementException(ErrorMessages.THERE_IS_NO_USER.getMessage() + loginEmail));
.orElseThrow(() -> new NoSuchElementException(ErrorMessages.THERE_IS_NO_USER.getMessage() + loginEmail));
return loginUser.getId();
}

// 공지 게시글 Dto
public PostDto toNoticeDto(PostEntity postEntity) {
return PostDto.builder()
.id(postEntity.getId())
.postTitle(postEntity.getPostTitle())
.postContent(postEntity.getPostContent())
.build();
}
}

4 changes: 2 additions & 2 deletions frontend/lib/global/http/http_request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Future<http.Response> sendHttpRequest(String method, Uri uri, {String? body, Map
if (response.statusCode == 200) { // 응답의 상태 코드가 200인 경우
return response; // 응답을 그대로 반환
} else if (response.statusCode == 401) { // 응답의 상태 코드가 401인 경우
if (jsonDecode(response.body)['code'] == 401) { // 응답 바디의 code가 401인 경우
if (jsonDecode(response.body)['code'] == 401) { // 응답 바디의 errorCode가 401인 경우
final refreshToken = prefs.getString('refreshToken');
final tokenResponse = await http.post(
// Uri.http(dotenv.env['API_URL']!, '/api/auth/refreshToken'),
Expand All @@ -57,7 +57,7 @@ Future<http.Response> sendHttpRequest(String method, Uri uri, {String? body, Map

// 재귀 호출하여 새로운 토큰으로 다시 HTTP 요청을 보냅니다.
return sendHttpRequest(method, uri, body: body, headers: headers);
} else if (jsonDecode(tokenResponse.body)['code'] == 402) { // 응답 바디의 code가 402인 경우
} else if (jsonDecode(tokenResponse.body)['code'] == 402) { // 응답 바디의 errorCode가 402인 경우
Set<String> keys = prefs.getKeys();

for (String key in keys) {
Expand Down
Loading

0 comments on commit 4b229dc

Please sign in to comment.