Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/jacoco.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ jobs:
title: Test Coverage Report
paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml
token: ${{ secrets.GITHUB_TOKEN }}
min-coverage-overall: 50
min-coverage-changed-files: 70
min-coverage-overall: 30
min-coverage-changed-files: 50
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ secrets.yml

.idea
.env
src/test/resources/application.yml
src/test/resources/application.yml
/uploads
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.PHONY: all build down re

all: build copy up

build:
@./gradlew clean build

copy:
@cp ./build/libs/backend-0.0.1-SNAPSHOT.jar ./app.jar

up:
@docker compose up --build -d

down:
@docker compose down

re: down up
Comment on lines +1 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 makefile 어디에 사용되는건가요??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 빌드하고 docker compose 돌리는거 귀찮아서 만들었어요😅

8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,17 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.postgresql:postgresql:42.7.3'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}

tasks.named('test') {
Expand All @@ -58,7 +63,8 @@ jacocoTestReport {
files(classDirectories.files.collect {
fileTree(dir: it, exclude: [
'**/Q*.class',
'**/run/backend/BackendApplication.class'
'**/run/backend/BackendApplication.class',
'**/run/backend/global/**'
])
})
)
Expand Down
28 changes: 28 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
services:
db:
image: postgres:15
restart: unless-stopped
container_name: runners_db
env_file: .env
ports:
- "5432:5432"
volumes:
- runners_db:/var/lib/postgresql/data

app:
image: openjdk:17-slim
container_name: runners_app
env_file: .env
ports:
- "8080:8080"
volumes:
- ./app.jar:/app/app.jar
- ./uploads:/app/uploads
depends_on:
- db
restart: unless-stopped
command: [ "java", "-jar", "/app/app.jar" ]

volumes:
runners_db:

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package run.backend.domain.auth.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
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.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import run.backend.domain.auth.dto.request.CodeRequest;
import run.backend.domain.auth.dto.request.SignupRequest;
import run.backend.domain.auth.dto.response.SignupResponse;
import run.backend.domain.auth.dto.response.TokenResponse;
import run.backend.domain.auth.service.AuthService;
import run.backend.global.common.response.CommonResponse;

@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthController {

private final AuthService authService;

@PostMapping("/{provider}")
public ResponseEntity<CommonResponse<SignupResponse>> socialLogin(
@PathVariable String provider, @RequestBody CodeRequest codeRequest) {

SignupResponse response = authService.socialLogin(provider, codeRequest.code());

return ResponseEntity.ok(new CommonResponse<>("소셜 로그인 요청에 성공했습니다.", response));
}

@PostMapping("/signup")
public ResponseEntity<CommonResponse<TokenResponse>> signup(
@RequestPart("signupRequest") SignupRequest signupRequest,
@RequestPart(value = "profileImage", required = false) MultipartFile profileImage) {

TokenResponse response = authService.completeSignup(signupRequest, profileImage);

return ResponseEntity.ok(new CommonResponse<>("회원가입이 완료되었습니다.", response));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accessToken 만료 되었을 때 재발급하는 api 가 없어서 이것만 있으면 될거 같아요!!
accessToken 발급할때 refeshToken도 새로 발급하는 RTR 방식 사용해도 좋을거 같아요!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재발급 api 까먹,, RTR 방식 찾아보고 수정할게요!!


@PostMapping("/refresh")
public ResponseEntity<CommonResponse<TokenResponse>> refresh(
@RequestHeader("Authorization") String authorizationHeader) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 대박 이렇게 가지고 올 수 있는거 처음 알았어요


TokenResponse response = authService.refreshTokens(authorizationHeader);

return ResponseEntity.ok(new CommonResponse<>("토큰이 갱신되었습니다.", response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package run.backend.domain.auth.dto.request;

public record CodeRequest(String code) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package run.backend.domain.auth.dto.request;

import run.backend.domain.member.enums.Gender;

public record SignupRequest(String signupToken, String nickname, Gender gender, int age) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package run.backend.domain.auth.dto.response;

public record SignupResponse(boolean isNewUser, String signupToken, String email, String name,
String provider, TokenResponse tokens) {

public static SignupResponse forExistingUser(TokenResponse tokens) {
//기존 회원은 토큰만 리턴
return new SignupResponse(false, null, null, null, null, tokens);
}

public static SignupResponse forNewUser(String signupToken, String email, String name,
String provider) {
//신규 회원은 회원가입을 위한 정보와 임시 토큰 리턴
return new SignupResponse(true, signupToken, email, name, provider, null);
}
}
Comment on lines +3 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null이 많긴 하지만 좋은거 같아요!!
null 없이 하려면 인터페이스 만들어서 forExistingUser 용 response, forNewUser 용 response 를 따로 구현하는 방법도 추천받았는데, 지금 방식이 더 나은 것 같아요

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package run.backend.domain.auth.dto.response;

public record TokenResponse(String accessToken, String refreshToken) {

}
Comment on lines +3 to +5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

토큰 헤더에 안 넣고 body에 담아서 보내주는게 더 나을까요??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앱은 어떻게 처리해야 될 지 몰라서 우선 body에 담았는데 한번 찾아볼게요!!

49 changes: 49 additions & 0 deletions src/main/java/run/backend/domain/auth/entity/RefreshToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package run.backend.domain.auth.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import run.backend.domain.member.entity.Member;

@Entity
@Table(name = "refresh_tokens")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RefreshToken {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, unique = true)
private String token;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
private Member member;

@Column(nullable = false)
private LocalDateTime expiresAt;

@Builder
public RefreshToken(String token, Member member, LocalDateTime expiresAt) {
this.token = token;
this.member = member;
this.expiresAt = expiresAt;
}

public boolean isExpired() {
return LocalDateTime.now().isAfter(expiresAt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package run.backend.domain.auth.repository;

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import run.backend.domain.auth.entity.RefreshToken;
import run.backend.domain.member.entity.Member;

public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {

Optional<RefreshToken> findByToken(String token);

@Modifying
@Query("DELETE FROM RefreshToken rt WHERE rt.member = :member")
void deleteByMember(@Param("member") Member member);

}
Loading