Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
d6837e7
docs: 의존관계 README.md 작성
devjun10 Feb 4, 2024
f6f0082
chore: application.yml 제거
devjun10 Feb 4, 2024
7b3b80a
chore: 의존성 추가
devjun10 Feb 4, 2024
c5490ac
chore: 데이터베이스 의존성 추가
devjun10 Feb 5, 2024
9b33c1e
feat: 서비스 프록시 클래스 추가
devjun10 Feb 5, 2024
43da321
refactor: Repository 구현체 교체
devjun10 Feb 5, 2024
21c72f1
feat: Transaction 연관 클래스 추가
devjun10 Feb 5, 2024
8538aea
feat: Jdbc 연관 클래스 추가
devjun10 Feb 5, 2024
00b4fb1
feat: InvalidDataAccessException 예외 추가
devjun10 Feb 5, 2024
ab6753d
feat: DataSource 연관 클래스 추가
devjun10 Feb 5, 2024
623ac9a
refactor: User 클래스 리팩토링
devjun10 Feb 5, 2024
73ec54e
refactor: AbstractHandlerMethodAdapter @Slf4j 추가
devjun10 Feb 5, 2024
f924765
refactor: Proxy를 구현체로 등록
devjun10 Feb 5, 2024
615d015
refactor: e.printStackTrace 메서드 제거
devjun10 Feb 5, 2024
8c67fa8
feat: .yml 파일 설정을 저장하기 위한 ConfigMap 클래스 추가
devjun10 Feb 5, 2024
9e0b32b
feat: 데이터베이스, Jdbc 설정 클래스 추가
devjun10 Feb 5, 2024
20d813d
docs: step3 README.md 정리
devjun10 Feb 6, 2024
383b6d9
chore: 데이터베이스 의존성 이동
devjun10 Feb 6, 2024
b2b7f53
test: 깨진 테스트 복구
devjun10 Feb 6, 2024
71f567b
refactor: jdbc 템플릿 구현 변경
devjun10 Feb 6, 2024
319fb6b
refactor: User 사용하지 않는 메서드 제거
devjun10 Feb 6, 2024
434d7b5
test: Jdbc 코드 변겨에 따른 사용자 통합 테스트 코드 변경
devjun10 Feb 12, 2024
b0a19e1
chore: Jackson 의존성 추가
devjun10 Feb 12, 2024
c7cd900
refactor: App 모듈 JdbcTemplate 리팩토링
devjun10 Feb 12, 2024
9a9132c
refactor: 예외 처리 리팩토링
devjun10 Feb 12, 2024
d8898ab
refactor: JdbcHelper 추가
devjun10 Feb 12, 2024
ba608fc
refactor: InvalidParameterException toString 재정의
devjun10 Feb 12, 2024
fb4183b
test: 사용자 단위 테스트 케이스 추가
devjun10 Feb 12, 2024
08c182b
refactor: 비동기 처리 응답 리팩토링
devjun10 Feb 12, 2024
fb4b4ba
refactor: Checkstyle, PMD 반영
devjun10 Feb 12, 2024
7824175
refactor: error 로그 추가
devjun10 Feb 12, 2024
25f932e
refactor: ThreadLocal 생성 로직 변경
devjun10 Feb 12, 2024
e211aee
test: 깨진 테스트 복구
devjun10 Feb 15, 2024
222b95b
refactor: exists 네이밍 변경
devjun10 Feb 15, 2024
a1daa1f
refactor: Http 클래스 리팩토링
devjun10 Feb 15, 2024
ce88f98
refactor: Login시 Redirect 하도록 리팩토링
devjun10 Feb 15, 2024
50e3d8a
refactor: 자원 처리 방식 변경
devjun10 Feb 15, 2024
171756c
refactor: Cookie 관련 헤더 설정 변경
devjun10 Feb 15, 2024
506f636
refactor: Http 및 포워딩 설정 변경
devjun10 Feb 15, 2024
5613715
refactor: 정적 자원 변경
devjun10 Feb 15, 2024
414e8a2
test: 깨진 테스트 복구
devjun10 Feb 18, 2024
dd213c8
chore: .yml 파일 다운로드 추가
devjun10 Feb 18, 2024
b93ca46
refactor: 정적 자원처리방식 변경
devjun10 Feb 18, 2024
c26c263
refactor: 클래스 명칭 변경
devjun10 Feb 18, 2024
70c9a64
refactor: URL 경로 변경
devjun10 Feb 18, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ out/

### VS Code ###
.vscode/
*.yml

Choose a reason for hiding this comment

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

.yml을 .gitignore에 추가해놓은 이유가 있나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

외부에 config 서버를 두고 설정 파일을 다운받아 사용할 예정이기에 yml 파일이 노출될 필요가 없기 때문입니다.

image

Choose a reason for hiding this comment

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

프로필을 분리해서 관리하는� 방식은 어떻게 생각하시나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

그것도 괜찮은 방식이죠. 👍 기존에 스프링을 사용할 때, 말씀하신 개발 환경에 따른 분리를 하기 때문에 이번 미션에선 다른 방법을 채택했습니다.

47 changes: 37 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,46 @@

사용자 정보를 바탕으로 로그인 기능을 구현한다.

1. 로그인 기능을 구현한다.
- 세션을 이용해 구현한다.
- 세션은 애플리케이션 내부에 저장/관리한다.
- 세션 유지 시간을 제한 한다.
- [선택] 최근 로그인 기록과 아이피를 식별할 수 있도록 한다.
2. 개인 정보 상세 조회 기능을 개발한다.
- [선택] 이미지 업로드 기능을 구현한다.
- [x] 로그인 기능을 구현한다.
- 세션을 이용해 구현한다.
- 세션은 애플리케이션 내부에 저장/관리한다.
- 세션 유지 시간을 제한 한다.
- [선택] 최근 로그인 기록과 아이피를 식별할 수 있도록 한다.

- [x] 개인 정보 상세 조회 기능을 개발한다.


<br>
<br>

### 학습 목표
- HTTP 특징에 대해 학습한다.

1. HTTP 특징에 대해 학습한다.
- 쿠키/세션에 대해 학습한다.
- 세션 관리 방법에 대해 학습한다.

2. 세션 관리 방법에 대해 학습한다.

<br>
<br>
<br>
<br>
<br>
<br>

## Step3. 데이터베이스를 교체한다.

애플리케이션 내부에 저장하던 데이터를 외부 데이터베이스에 저장한다.

1. 데이터베이스 종류는 자유롭게 선택 한다.
- RDB, Redis 등
2. JDBC 템플릿을 구현한다.

<br>
<br>

### 학습 목표

1. 추상화에 대해 이해한다.
2. 데이터베이스 통신 과정에 대해 이해한다.
3. 각 데이터베이스의 특징에 대해 이해한다.
4. 트랜잭션에 대해 학습한다.
19 changes: 19 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## ⛺️ Application 모듈

Application 모듈.

<br/><br/><br/>

## 👪 패키지 간 의존관계

Application 모듈은 Mvc, Jdbc 모듈에 의존합니다.

| Application Module | Mvc Module | Jdbc Module |
|:------------------:|:----------:|:-----------:|
| - | O | O |

&nbsp;&nbsp; - Application: 애플리케이션 모듈 <br/>
&nbsp;&nbsp; - Mvc: 스프링 Mvc 모듈 <br/>
&nbsp;&nbsp; - Jdbc: 데이터베이스 접근 모듈 <br/>

<br/>
26 changes: 26 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ plugins {

dependencies {
implementation(project(":mvc"))
implementation(project(":jdbc"))

implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.1")
runtimeOnly("com.mysql:mysql-connector-j:8.1.0")
}

Choose a reason for hiding this comment

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

추가하신 각각의 의존성에 대해서 설명해주세요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

jackson-databind는 자바 객체 <-> Json 객체를 변환해 주는 의존성입니다. 이를 통해 자바 객체를 Json 데이터로 직렬화/역 직렬화할 수 있습니다. 그런데 코드를 제출하고 보니, 굳이 해당 의존성은 사용할 필요는 없을 것 같네요.


jackson-dataformat-yaml는 yaml 데이터 형식을 객체로 직렬화/역 직렬화 해주는 의존성입니다. ConfigMap을 통해 .yml 파일을 객체로 만들고 있는데요, 이를 통해 MySQL 데이터베이스와 연동하기 위함입니다.

  1. yml파일을 자바 객체에 바인딩
  2. 설정 정보를 통해 MySQL과 연동
  3. 데이터베이스 연동

Copy link
Contributor Author

Choose a reason for hiding this comment

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

다시 생각해보니 jackson-databind는 사용할 필요가 없겠네요. 👀 제외하겠습니다.


tasks.named("test") {
Expand Down Expand Up @@ -68,3 +72,25 @@ sonarqube {
property("sonar.coverage.jacoco.xmlReportPaths", "${buildDir}/jacoco/index.xml")
}
}

task downloadYml {
doLast {
def url = new URL(System.getenv("YML_URL"))
def connection = url.openConnection()

def file = new File(projectDir, "./src/main/resources/application.yml")
if (!file.parentFile.exists()) {
file.parentFile.mkdirs()
}

connection.inputStream.withStream { inputStream ->
file.withOutputStream { outputStream ->
inputStream.transferTo(outputStream)
}
}
}
}

tasks.named("downloadYml") {
dependsOn downloadYml
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ public enum ErrorCodeAndMessages implements ErrorCodeAndMessage {
INVALID_SESSION(HttpStatus.UN_AUTHORIZED, "세션이 만료되었습니다."),
INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "올바른 값을 입력해주세요."),
PAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "페이지를 찾을 수 없습니다."),
UN_AUTHORIZED(HttpStatus.UN_AUTHORIZED, "권한이 존재하지 않습니다.");
UN_AUTHORIZED(HttpStatus.UN_AUTHORIZED, "권한이 존재하지 않습니다."),
INVALID_DATA_ACCESS(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다.");

private final HttpStatus httpStatus;
private final String errorMessage;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package project.server.app.common.configuration;

import com.fasterxml.jackson.databind.ObjectMapper;
import static com.fasterxml.jackson.databind.PropertyNamingStrategies.KEBAB_CASE;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.IOException;
import java.io.InputStream;
import project.server.jdbc.core.ConfigMap;

public class ConfigMapLoader {

public ConfigMap getConfigMap() throws IOException {
return loadConfig("application.yml");
}

public ConfigMap loadConfig(String resourcePath) throws IOException {
InputStream inputStream = getInputStream(resourcePath);
if (inputStream == null) {
throw new IOException("Resource not found: " + resourcePath);
}

ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
mapper.setPropertyNamingStrategy(KEBAB_CASE);
return mapper.readValue(inputStream, ConfigMap.class);
}

private InputStream getInputStream(String resourcePath) {
return ConfigMapLoader.class.getClassLoader()
.getResourceAsStream(resourcePath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package project.server.app.common.configuration;

import java.io.IOException;
import project.server.jdbc.core.ConfigMap;
import project.server.jdbc.core.DriverManager;
import project.server.jdbc.core.jdbc.JdbcTemplate;
import project.server.jdbc.core.transaction.JdbcTransactionManager;
import project.server.jdbc.core.transaction.PlatformTransactionManager;
import project.server.mvc.springframework.annotation.Bean;
import project.server.mvc.springframework.annotation.Configuration;

@Configuration
public class DatabaseConfiguration {

@Bean
public ConfigMapLoader configMapLoader() {
return new ConfigMapLoader();
}

@Bean
public DriverManager driverManager() throws IOException {
ConfigMap configMap = configMapLoader().getConfigMap();
return new DriverManager(configMap);
}

@Bean
public PlatformTransactionManager transactionManager() throws IOException {
return new JdbcTransactionManager(driverManager().getDataSource());
}

@Bean
public JdbcTemplate jdbcTemplate() throws IOException {
return new JdbcTemplate(driverManager().getDataSource());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package project.server.app.common.exception;

import project.server.app.common.codeandmessage.ErrorCodeAndMessage;
import project.server.mvc.servlet.http.HttpStatus;

public class BusinessException extends RuntimeException {

Expand All @@ -14,4 +15,14 @@ public BusinessException(ErrorCodeAndMessage errorCodeAndMessage) {
public ErrorCodeAndMessage getCodeAndMessage() {
return codeAndMessage;
}

@Override
public String toString() {
HttpStatus status = codeAndMessage.getStatus();
return String.format(
"{\"code\":%d, \"message\":\"%s\"}",
status.getStatusCode(),
codeAndMessage.getErrorMessage()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package project.server.app.common.exception;

import project.server.app.common.codeandmessage.failure.ErrorCodeAndMessages;
import project.server.mvc.servlet.http.HttpStatus;

public class InvalidParameterException extends RuntimeException {

Expand All @@ -19,4 +20,14 @@ public ErrorCodeAndMessages getErrorCodeAndMessages() {
public Object getArgs() {
return args;
}

@Override
public String toString() {
HttpStatus status = errorCodeAndMessages.getStatus();
return String.format(
"{\"code\":%d, \"message\":\"%s\"}",
status.getStatusCode(),
errorCodeAndMessages.getErrorMessage()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import static project.server.app.common.codeandmessage.failure.ErrorCodeAndMessages.UN_AUTHORIZED;

public class UnAuthorizedException extends RuntimeException {
public class UnAuthorizedException extends BusinessException {

public UnAuthorizedException() {
super(UN_AUTHORIZED.getErrorMessage());
super(UN_AUTHORIZED);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package project.server.app.common.utils;

import project.server.mvc.springframework.annotation.Configuration;

@Configuration
public class JdbcConfiguration {
}
22 changes: 11 additions & 11 deletions app/src/main/java/project/server/app/core/domain/user/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,27 @@ public String getPassword() {
return password.value();
}

public Deleted getDeleted() {
return deleted;
public LocalDateTime getCreatedAt() {
return createdAt;
}

public boolean isNew() {
return this.id == null;
public LocalDateTime getLastModifiedAt() {
return lastModifiedAt;
}

public void registerId(Long id) {
this.id = id;
public Deleted getDeleted() {
return deleted;
}

public boolean isAlreadyDeleted() {
return this.deleted.equals(Deleted.TRUE);
}

public void delete(LocalDateTime lastModifiedAt) {
this.lastModifiedAt = lastModifiedAt;
this.deleted = Deleted.TRUE;
}

@Override
public boolean equals(Object object) {
if (this == object) {
Expand All @@ -87,11 +92,6 @@ public boolean equals(Object object) {
return getId().equals(user.getId());
}

public void delete(LocalDateTime lastModifiedAt) {
this.lastModifiedAt = lastModifiedAt;
this.deleted = Deleted.TRUE;
}

@Override
public int hashCode() {
return Objects.hash(getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
import java.util.Optional;

public interface UserRepository {
User save(User user);
Long save(User user);

Optional<User> findById(Long userId);

void clear();

boolean existByName(String username);

List<User> findAll();
boolean existsByName(String username);

Optional<User> findByUsernameAndPassword(String username, String password);

void delete(User user);

List<User> findAll();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package project.server.app.core.web.user.application;

import project.server.app.core.domain.user.User;

public interface UserSaveUseCase {
User save(User user);
Long save(String username, String password);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@ public Session login(

@Override
public Session findSessionById(Long userId) {
Session findSession = sessionManager.findByUserId(userId)
.orElseThrow(UnAuthorizedException::new);
try {
Session findSession = sessionManager.findByUserId(userId)
.orElseThrow(UnAuthorizedException::new);

if (!findSession.isValid(now())) {
throw new SessionExpiredException();
if (!findSession.isValid(now())) {
throw new SessionExpiredException();
}
return findSession;
} catch (UnAuthorizedException | SessionExpiredException exception) {
return null;
}
return findSession;
}
}
Loading