-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/woowacourse-teams/2024-corea
- Loading branch information
Showing
165 changed files
with
3,741 additions
and
663 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,86 @@ | ||
# Code Review Area | ||
|
||
<br> | ||
코드 리뷰 파트너 자동 매칭 플랫폼 **CoReA**로 완성하는 성장의 퍼즐 <br> | ||
: **코드, 리뷰, 그리고 당신** | ||
|
||
혼자 공부하다 보면 막막할 때가 많습니다. | ||
|
||
<br> | ||
<br> | ||
|
||
> '내가 지금 제대로 공부하고 있는 건가?' <br> | ||
> '남들은 내 코드를 어떻게 생각하고 있을까?' <br> | ||
> '좋은 코드에 대한 평가 기준은 없을까?' | ||
> | ||
<br> | ||
|
||
저희도 그랬습니다. *옛날에는요.* | ||
그렇다면 `나와 함께 성장할 동료 개발자`들은 어디서 구할 수 있을까요? | ||
|
||
- 학교? 🏫 | ||
- 부트캠프? 🏕️ | ||
- 커뮤니티? 🤼♀️ | ||
|
||
<br> | ||
|
||
## 함께 일하는 법 | ||
**CoReA**에서는 별도 구인 과정 없이도 자동 매칭된 동료 개발자와 지속적인 피드백을 통해 **함께 성장**할 수 있어요. <br> | ||
**많은 사람들과 코드 리뷰**를 주고 받으며 다양한 관점을 발견해보세요! | ||
|
||
<br> | ||
<br> | ||
|
||
어떤 사람을 우리는 좋은 개발자라고 말할까요? <br> | ||
# 프로젝트 설명 및 사용법 | ||
|
||
다양한 연차의 개발자를 대상으로 진행된 한 인터뷰에서는 많은 사람들이 함께하고 싶은 동료로 <br> | ||
`함께 성장하기 위해 노력하는 사람`, `근거와 함께 의견을 설득하는 사람` 등을 꼽았어요. | ||
## 1. 미션 참여 | ||
|
||
<br> | ||
코레아는 같은 미션을 각자 해결하고, 다른 사람들과 공유하고 코드리뷰를 통해 의견을 나누는 환경을 제공하는 곳이에요. | ||
원하는 미션을 선택하고 방 세부 정보를 확인한 후 참여하세요. | ||
|
||
그리고 그건 기업에서도 동일합니다.<br> | ||
<img width="2055" alt="dd1" src="https://github.com/user-attachments/assets/442a18f0-bb22-470c-a863-b29584969e6c"> | ||
|
||
[기사](https://www.samsungsds.com/kr/insights/global_code_review.html)에서는 다음과 같이 말하고 있어요. | ||
## 2. 리뷰어, 리뷰이 매칭 | ||
|
||
<br> | ||
모집 마감 시간이 되면 방에 기재된 깃허브주소에 PR을 작성한 사람들끼리 리뷰어와 리뷰이가 자동으로 매칭돼요. | ||
|
||
>100% 코드 리뷰, 모든 코드를 리뷰하는 **Google**<br> | ||
개발자 업무는 개발 50%, 리뷰 50%인 **Microsoft**<br> | ||
“리뷰는 당연하다”, **네카라쿠배** | ||
<img width="1787" alt="dd2" src="https://github.com/user-attachments/assets/3e581d2b-2ff0-4cb4-8d2f-b896f6ec97b0"> | ||
|
||
<br> | ||
## 3. 코드리뷰 | ||
|
||
`코드 리뷰`, 그리고 `함께`.<br> | ||
매칭된 리뷰이의 PR 링크에서 깃허브 코드리뷰를 진행해요. | ||
|
||
다 좋아요. | ||
<img width="1757" alt="dd3" src="https://github.com/user-attachments/assets/746f74b4-748c-4445-afea-7daf139d06ca"> | ||
|
||
하지만 가장 중요한, `내가 함께 코드 리뷰를 할 동료 개발자`들은 어디서 구할 수 있을까요? | ||
## 4. 피드백 작성 | ||
|
||
- 학교? 🏫 | ||
- 부트캠프? 🏕️ | ||
- 커뮤니티? 🤼♀️ | ||
두가지 방면에서 피드백을 남길 수 있어요. | ||
|
||
<img width="1901" alt="dd4" src="https://github.com/user-attachments/assets/5f832e88-f3dc-45e9-a451-4866c6680ffc"> | ||
|
||
### 1. 리뷰어가 리뷰이에게 남기는 개발 피드백 | ||
|
||
코드리뷰를 마치고 코드리뷰 완료 버튼을 클릭하면 피드백을 작성할 수 있어요. | ||
리뷰어의 코드리뷰에 대한 피드백을 남겨주세요. | ||
|
||
### 2. 리뷰이가 리뷰어에게 남기는 소셜 피드백 | ||
|
||
리뷰어가 코드리뷰를 완료하면 피드백 작성 버튼이 활성화돼요. | ||
버튼을 클릭하여 상대방의 리뷰에 대한 피드백을 남겨주세요. | ||
|
||
## 5. 피드백 확인 | ||
|
||
나쁘지 않습니다. | ||
피드백 모아보기에서 주고받은 모든 피드백을 확인할 수 있어요. | ||
|
||
하지만 매번 나의 코드를 리뷰해줄 개발자를 일일이 구인하는 건 어려운 일이에요.<br> | ||
리뷰해주겠다는 약속을 지키지 않는 사람들도 많고요. | ||
<img width="2009" alt="dd5" src="https://github.com/user-attachments/assets/8c6af2e0-73aa-493d-9449-1b93ce679c4c"> | ||
|
||
<br><br> | ||
|
||
# 기술 스택 소개 | ||
|
||
![dd](https://github.com/user-attachments/assets/a2411aee-bca9-4dc4-9332-87460e3e4f0f) | ||
|
||
<br> | ||
<br> | ||
|
||
나에게 도움을 줄 수 있는 개발자이자 리뷰어를 `코레아`에서 찾아보는 건 어떠세요?<br> | ||
또한 개발자의 코드와 리뷰어의 피드백을 통해 폭발적인 성장을 이루어보아요! | ||
# 팀원 소개 | ||
|
||
코드에 대해 다양한 사람들과 의견을 얘기하며 내가 생각하지 못한 부분을 발견해보세요!<br> | ||
그리고 리뷰어에게 피드백을 받아서 성장을 이뤄나가요! 🙂🙂 | ||
| <img src="https://github.com/user-attachments/assets/b29d41fa-2f5e-40ac-b5cd-531f0edf0aaa" width="100px"/> | <img src="https://github.com/user-attachments/assets/ce1dabf2-240c-48a9-b6db-0fa22811444b" width="100px"/> | <img src="https://github.com/user-attachments/assets/882edb5c-6cb0-4745-b22f-c292847a4e89" width="100px"/> | <img src="https://github.com/user-attachments/assets/0ddf7b9a-6914-4877-bdd7-924fb485285d" width="100px"/> | <img src="https://github.com/user-attachments/assets/3eda6b53-d7ca-48c9-b182-11d2bdcbacd4" width="100px"/> | <img src="https://github.com/user-attachments/assets/b1a303da-739b-4541-bc0e-7c444310521e" width="100px"/> | <img src="https://github.com/user-attachments/assets/2062bcff-d28f-454f-a7cd-e0f8b9aba823" width="100px"/> | | ||
| :---------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------: | | ||
| [<img src="https://cdn-icons-png.flaticon.com/512/2111/2111432.png" width="15px" height="15px" /> FE\_텐텐](https://github.com/chlwlstlf) | [<img src="https://cdn-icons-png.flaticon.com/512/2111/2111432.png" width="15px" height="15px" /> FE\_다르](https://github.com/pp449) | [<img src="https://cdn-icons-png.flaticon.com/512/2111/2111432.png" width="15px" height="15px" /> FE\_초코](https://github.com/00kang) | [<img src="https://cdn-icons-png.flaticon.com/512/2111/2111432.png" width="15px" height="15px" /> BE\_애쉬](https://github.com/ashsty) | [<img src="https://cdn-icons-png.flaticon.com/512/2111/2111432.png" width="15px" height="15px" /> BE\_무빈](https://github.com/hjk0761) | [<img src="https://cdn-icons-png.flaticon.com/512/2111/2111432.png" width="15px" height="15px" /> BE\_뽀로로](https://github.com/jcoding-play) | [<img src="https://cdn-icons-png.flaticon.com/512/2111/2111432.png" width="15px" height="15px" /> BE\_조이썬](https://github.com/youngsu5582) | |
40 changes: 40 additions & 0 deletions
40
backend/src/main/java/corea/alarm/controller/AlarmController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package corea.alarm.controller; | ||
|
||
import corea.alarm.dto.AlarmCheckRequest; | ||
import corea.alarm.dto.AlarmCountResponse; | ||
import corea.alarm.dto.AlarmResponses; | ||
import corea.alarm.service.AlarmService; | ||
import corea.auth.annotation.LoginMember; | ||
import corea.auth.domain.AuthInfo; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
|
||
@Controller | ||
@RequiredArgsConstructor | ||
public class AlarmController implements AlarmControllerSpecification { | ||
|
||
private final AlarmService alarmService; | ||
|
||
@GetMapping("/alarm/count") | ||
public ResponseEntity<AlarmCountResponse> getAlarmCount(@LoginMember AuthInfo authInfo) { | ||
AlarmCountResponse response = alarmService.getUnReadAlarmCount(authInfo.getId()); | ||
return ResponseEntity.ok(response); | ||
} | ||
|
||
@GetMapping("/alarm") | ||
public ResponseEntity<AlarmResponses> getAlarms(@LoginMember AuthInfo authInfo) { | ||
AlarmResponses responses = alarmService.getAlarm(authInfo.getId()); | ||
return ResponseEntity.ok(responses); | ||
} | ||
|
||
@PostMapping("/alarm/check") | ||
public ResponseEntity<Void> checkAlarm(@LoginMember AuthInfo authInfo, @RequestBody AlarmCheckRequest request) { | ||
alarmService.checkAlarm(authInfo.getId(), request); | ||
return ResponseEntity.ok() | ||
.build(); | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
backend/src/main/java/corea/alarm/controller/AlarmControllerSpecification.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package corea.alarm.controller; | ||
|
||
import corea.alarm.dto.AlarmCheckRequest; | ||
import corea.alarm.dto.AlarmCountResponse; | ||
import corea.alarm.dto.AlarmResponses; | ||
import corea.auth.domain.AuthInfo; | ||
import corea.exception.ExceptionType; | ||
import corea.global.annotation.ApiErrorResponses; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import org.springframework.http.ResponseEntity; | ||
|
||
@Tag(name = "Alarm", description = "알림 관련 API") | ||
public interface AlarmControllerSpecification { | ||
|
||
@Operation(summary = "읽지 않은 알림 개수를 반환합니다.", | ||
description = "자신의 마이페이지에 디스플레이 되는 프로필 정보를 작성합니다. <br>" + | ||
"요청 시 `Authorization Header`에 `Bearer JWT token`을 포함시켜야 합니다. " + | ||
"이 토큰을 기반으로 `AuthInfo` 객체가 생성되며 사용자의 정보가 자동으로 주입됩니다. <br>" + | ||
"JWT 토큰에서 추출된 사용자 정보는 피드백 작성에 필요한 인증된 사용자 정보를 제공합니다. " + | ||
"<br><br>**참고:** 이 API를 사용하기 위해서는 유효한 JWT 토큰이 필요하며, " + | ||
"토큰이 없거나 유효하지 않은 경우 인증 오류가 발생합니다.") | ||
@ApiErrorResponses(value = ExceptionType.MEMBER_NOT_FOUND) | ||
ResponseEntity<AlarmCountResponse> getAlarmCount(AuthInfo authInfo); | ||
|
||
@Operation(summary = "알림 목록을 최신순으로 반환합니다.", | ||
description = "자신의 마이페이지에 디스플레이 되는 프로필 정보를 작성합니다. <br>" + | ||
"요청 시 `Authorization Header`에 `Bearer JWT token`을 포함시켜야 합니다. " + | ||
"이 토큰을 기반으로 `AuthInfo` 객체가 생성되며 사용자의 정보가 자동으로 주입됩니다. <br>" + | ||
"JWT 토큰에서 추출된 사용자 정보는 피드백 작성에 필요한 인증된 사용자 정보를 제공합니다. " + | ||
"<br><br>**참고:** 이 API를 사용하기 위해서는 유효한 JWT 토큰이 필요하며, " + | ||
"토큰이 없거나 유효하지 않은 경우 인증 오류가 발생합니다.") | ||
@ApiErrorResponses(value = ExceptionType.MEMBER_NOT_FOUND) | ||
ResponseEntity<AlarmResponses> getAlarms(AuthInfo authInfo); | ||
|
||
@Operation(summary = "알림을 체크합니다.", | ||
description = "자신의 마이페이지에 디스플레이 되는 프로필 정보를 작성합니다. <br>" + | ||
"요청 시 `Authorization Header`에 `Bearer JWT token`을 포함시켜야 합니다. " + | ||
"이 토큰을 기반으로 `AuthInfo` 객체가 생성되며 사용자의 정보가 자동으로 주입됩니다. <br>" + | ||
"JWT 토큰에서 추출된 사용자 정보는 피드백 작성에 필요한 인증된 사용자 정보를 제공합니다. " + | ||
"<br><br>**참고:** 이 API를 사용하기 위해서는 유효한 JWT 토큰이 필요하며, " + | ||
"토큰이 없거나 유효하지 않은 경우 인증 오류가 발생합니다.") | ||
@ApiErrorResponses(value = {ExceptionType.MEMBER_NOT_FOUND, ExceptionType.NOT_RECEIVED_ALARM}) | ||
ResponseEntity<Void> checkAlarm(AuthInfo authInfo, AlarmCheckRequest request); | ||
} |
6 changes: 6 additions & 0 deletions
6
backend/src/main/java/corea/alarm/domain/AlarmActionType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package corea.alarm.domain; | ||
|
||
public enum AlarmActionType { | ||
REVIEW_COMPLETE, | ||
REVIEW_URGE, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package corea.alarm.domain; | ||
|
||
public enum AlarmType { | ||
// 유저간 상호작용으로 인한 알람 | ||
USER; | ||
|
||
public static AlarmType from(String value) { | ||
return AlarmType.valueOf(value); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
backend/src/main/java/corea/alarm/domain/UserAlarmsByActionType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package corea.alarm.domain; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
public record UserAlarmsByActionType(Map<AlarmActionType, List<UserToUserAlarm>> data) { | ||
public Set<Long> getActorIds() { | ||
return data.values() | ||
.stream() | ||
.flatMap(Collection::stream) | ||
.map(UserToUserAlarm::getActorId) | ||
.collect(Collectors.toSet()); | ||
} | ||
|
||
public Set<Long> getRoomIds() { | ||
return data.values() | ||
.stream() | ||
.flatMap(Collection::stream) | ||
//.filter(alarm -> alarm.getAlarmActionType() == AlarmActionType.REVIEW_COMPLETE) | ||
.map(UserToUserAlarm::getInteractionId) | ||
.collect(Collectors.toSet()); | ||
} | ||
|
||
public List<UserToUserAlarm> getList() { | ||
return data.values() | ||
.stream() | ||
.flatMap(Collection::stream) | ||
.toList(); | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
backend/src/main/java/corea/alarm/domain/UserToUserAlarm.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package corea.alarm.domain; | ||
|
||
import corea.global.BaseTimeEntity; | ||
import corea.member.domain.Member; | ||
import jakarta.persistence.*; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
import static jakarta.persistence.GenerationType.IDENTITY; | ||
|
||
@Entity | ||
@AllArgsConstructor | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Getter | ||
public class UserToUserAlarm extends BaseTimeEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = IDENTITY) | ||
private Long id; | ||
|
||
@Enumerated(EnumType.STRING) | ||
private AlarmActionType alarmActionType; | ||
|
||
/** | ||
* C 라는 방에서 A 가 코드 리뷰를 B 에게 완료했다. | ||
* A 의 ID 가 actorId, B 의 ID 가 receiverId | ||
* C 의 ID 가 interactionId | ||
*/ | ||
private Long actorId; | ||
|
||
private Long receiverId; | ||
|
||
private Long interactionId; | ||
|
||
private boolean isRead; | ||
|
||
public UserToUserAlarm(AlarmActionType alarmActionType, long actorId, long receiverId, long interactionId, boolean isRead) { | ||
this(null, alarmActionType, actorId, receiverId, interactionId, isRead); | ||
} | ||
|
||
public boolean isStatus(boolean status) { | ||
return isRead == status; | ||
} | ||
|
||
public String getActionType() { | ||
return alarmActionType.name(); | ||
} | ||
|
||
public boolean isNotReceiver(Member member) { | ||
return !receiverId.equals(member.getId()); | ||
} | ||
|
||
public void read() { | ||
isRead = true; | ||
} | ||
|
||
public boolean isUrgeAlarm() { | ||
return alarmActionType == AlarmActionType.REVIEW_URGE; | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
backend/src/main/java/corea/alarm/domain/UserToUserAlarmReader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package corea.alarm.domain; | ||
|
||
import corea.exception.CoreaException; | ||
import corea.exception.ExceptionType; | ||
import corea.global.annotation.Reader; | ||
import corea.member.domain.Member; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
import java.util.EnumMap; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
@Reader | ||
@RequiredArgsConstructor | ||
public class UserToUserAlarmReader { | ||
|
||
private final UserToUserAlarmRepository userToUserAlarmRepository; | ||
|
||
public long countReceivedAlarm(Member member, boolean isRead) { | ||
return userToUserAlarmRepository.findAllByReceiverId(member.getId()) | ||
.stream() | ||
.filter(alarm -> alarm.isStatus(isRead)) | ||
.count(); | ||
} | ||
|
||
public UserToUserAlarm find(long actionId) { | ||
return userToUserAlarmRepository.findById(actionId) | ||
.orElseThrow(() -> new CoreaException(ExceptionType.NOT_RECEIVED_ALARM)); | ||
} | ||
|
||
public UserAlarmsByActionType findAllByReceiver(Member member) { | ||
return new UserAlarmsByActionType(userToUserAlarmRepository.findAllByReceiverId(member.getId()) | ||
.stream() | ||
.collect(Collectors.groupingBy( | ||
UserToUserAlarm::getAlarmActionType, | ||
() -> new EnumMap<>(AlarmActionType.class), | ||
Collectors.toList() | ||
))); | ||
} | ||
|
||
public boolean existUnReadUrgeAlarm(long revieweeId, long reviewerId, long roomId) { | ||
List<UserToUserAlarm> alarm = userToUserAlarmRepository.findAllByActorIdAndReceiverIdAndInteractionId(revieweeId, reviewerId, roomId); | ||
return alarm.stream() | ||
.anyMatch(userToUserAlarm -> userToUserAlarm.isUrgeAlarm() && !userToUserAlarm.isRead()); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
backend/src/main/java/corea/alarm/domain/UserToUserAlarmRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package corea.alarm.domain; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
import java.util.List; | ||
|
||
public interface UserToUserAlarmRepository extends JpaRepository<UserToUserAlarm, Long> { | ||
|
||
List<UserToUserAlarm> findAllByReceiverId(long receiverId); | ||
|
||
List<UserToUserAlarm> findAllByActorIdAndReceiverIdAndInteractionId(long actorId, long receiverId, long interactionId); | ||
} |
Oops, something went wrong.