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
11 changes: 11 additions & 0 deletions src/main/java/app/db/UserRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package app.db;

import app.model.User;
import database.ConnectionManager;
import database.CrudRepository;

public class UserRepository extends CrudRepository<User> {
public UserRepository(ConnectionManager connectionManager) {
super(connectionManager, User.class);
}
}
54 changes: 47 additions & 7 deletions src/main/java/app/handler/RegisterWithPost.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package app.handler;

import app.db.Database;
import app.db.UserRepository;
import app.model.User;
import config.VariableConfig;
import exception.ErrorCode;
import exception.ServiceException;
import http.HttpMethod;
Expand All @@ -14,19 +15,58 @@
import web.response.RedirectResponse;

public class RegisterWithPost extends SingleArgHandler<QueryParameters> {
private static final String EMAIL = "email";
private static final String NICKNAME = "nickname";
private static final String PASSWORD = "password";

private static final Logger log = LoggerFactory.getLogger(RegisterWithPost.class);

public RegisterWithPost() {
private final UserRepository userRepository;

public RegisterWithPost(UserRepository userRepository) {
super(HttpMethod.POST, "/user/create");
this.userRepository = userRepository;
}

@Override
public HandlerResponse handle(QueryParameters params) {
String email = params.getQueryValue("email").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "email required"));
String nickname = params.getQueryValue("nickname").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "nickname required"));
String password = params.getQueryValue("password").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "password required"));
Database.addUser(new User(password, nickname, email, UserRole.MEMBER.toString()));
log.info("Registered - password:{}, nickname:{}, email:{}", password, nickname, email);
String email = getRequired(params, EMAIL);
String nickname = getRequired(params, NICKNAME);
String password = getRequired(params, PASSWORD);

validate(email, nickname, password);

User saved = userRepository.save(
new User(password, nickname, email, UserRole.MEMBER.toString()));

log.info("Registered id:{}, email:{}, nickname:{}, password:{}",
saved.getId(), email, nickname, password);
Comment on lines +37 to +43

Choose a reason for hiding this comment

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

문제: 로그 메시지에 민감한 정보(비밀번호)가 포함되어 있습니다. 본 PR에서는 로그 출력을 추가했는데, 비밀번호를 그대로 출력하는 것은 보안 위험입니다.

제안: 비밀번호를 로그에 기록하지 않으세요:

log.info("Registered id:{}, email:{}, nickname:{}", saved.getId(), email, nickname);

return RedirectResponse.to("/login");
}

private void validate(String email, String nickname, String password) {
validateDuplicate(email, nickname);
validateLength(email, VariableConfig.EMAIL_MIN, VariableConfig.EMAIL_MAX, ErrorCode.EMAIL_LENGTH_INVALID);
validateLength(nickname, VariableConfig.NICKNAME_MIN, VariableConfig.NICKNAME_MAX, ErrorCode.NICKNAME_LENGTH_INVALID);
validateLength(password, VariableConfig.PASSWORD_MIN, VariableConfig.PASSWORD_MAX, ErrorCode.PASSWORD_LENGTH_INVALID);

Choose a reason for hiding this comment

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

문제: findByColumn 호출 시 문자열 상수가 사용되고 있습니다. 클래스 최상단에 이미 EMAIL, NICKNAME 상수가 정의되어 있는데, 여기서는 직접 문자열을 사용하고 있습니다.

제안: 이미 정의된 상수를 활용하도록 수정하세요:

if (!userRepository.findByColumn(EMAIL, email).isEmpty())

}

private String getRequired(QueryParameters params, String key) {
return params.getQueryValue(key)
.orElseThrow(() -> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, key + " required"));
}

private void validateLength(String value, int min, int max, ErrorCode code) {
int len = value.length();
if (len < min || len > max)
throw new ServiceException(code);
}

private void validateDuplicate(String email, String nickname) {
if (!userRepository.findByColumn(EMAIL, email).isEmpty())
throw new ServiceException(ErrorCode.EMAIL_ALREADY_EXISTS);

if (!userRepository.findByColumn(NICKNAME, nickname).isEmpty())
throw new ServiceException(ErrorCode.NICKNAME_ALREADY_EXISTS);
}
}
3 changes: 3 additions & 0 deletions src/main/java/app/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public User(String password, String nickname, String email, String userRole) {
this.userRole = userRole;
}

public User() {
}

public Long getId() {
return id;
}
Expand Down
23 changes: 13 additions & 10 deletions src/main/java/config/AppConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package config;

import app.db.UserRepository;
import app.handler.*;
import database.ConnectionManager;
import database.H2DbManager;
Expand Down Expand Up @@ -120,8 +121,7 @@ public RegisterWithGet registerWithGet() {
public RegisterWithPost registerWithPost() {
return getOrCreate(
"registerWithPost",
RegisterWithPost::new
);
() -> new RegisterWithPost(userRepository()));
}

public LoginWithPost loginWithPost() {
Expand Down Expand Up @@ -233,8 +233,9 @@ public QueryParamsResolver queryParamsResolver() {
}

public MultipartFormResolver multipartFormResolver(){
return getOrCreate("multipartFormResolver", () ->
new MultipartFormResolver(multipartFormParser()));
return getOrCreate("multipartFormResolver",
() -> new MultipartFormResolver(multipartFormParser()));

}

public MultipartFormParser multipartFormParser(){
Expand All @@ -250,10 +251,7 @@ public ExceptionHandlerMapping exceptionHandlerMapping() {
List.of(
serviceExceptionHandler(),
errorExceptionHandler(),
unhandledErrorHandler()
)
)
);
unhandledErrorHandler())));
}

public ServiceExceptionHandler serviceExceptionHandler() {
Expand Down Expand Up @@ -325,8 +323,13 @@ public H2DbManager h2DbManager(){
}

public DdlGenerator ddlGenerator(){
return getOrCreate(DdlGenerator.class.getSimpleName(), () ->
new DdlGenerator(connectionManager()));
return getOrCreate(DdlGenerator.class.getSimpleName(),
() -> new DdlGenerator(connectionManager()));
}

public UserRepository userRepository(){
return getOrCreate(UserRepository.class.getSimpleName(),
()-> new UserRepository(connectionManager()));
}
}

7 changes: 6 additions & 1 deletion src/main/java/config/DdlGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private String buildDdlForEntity(Class<?> entityClass, String tableName) {
continue;
}

String columnName = field.getName();
String columnName = toColumnName(field.getName());
Class<?> fieldType = field.getType();

if (!firstColumn) {
Expand Down Expand Up @@ -108,4 +108,9 @@ private String toTableName(Class<?> clazz) {
}
return name;
}

private String toColumnName(String str){
String snake = str.replaceAll("(?<!^)([A-Z])", "_$1");
return snake.toLowerCase();
}
}
7 changes: 7 additions & 0 deletions src/main/java/config/VariableConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ public class VariableConfig {

public static final long IDLE_MS = 30*60*100;
public static final long ABSOLUTE_MS = 180*60*100;

public static final int EMAIL_MAX = 50;
public static final int EMAIL_MIN = 4;
public static final int NICKNAME_MAX = 12;
public static final int NICKNAME_MIN = 4;
public static final int PASSWORD_MAX = 16;
public static final int PASSWORD_MIN = 4;
Comment on lines +19 to +21

Choose a reason for hiding this comment

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

제안: 빈 줄 처리. 가독성 개선을 위해 상수 그룹 사이의 빈 줄을 하나로 줄이세요:

public static final int EMAIL_MAX = 50;
public static final int EMAIL_MIN = 4;

}
19 changes: 12 additions & 7 deletions src/main/java/database/CrudRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ public T save(T entity) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if ("id".equals(field.getName())) {
if ("id".equals(toColumnName(field.getName()))) {
continue;
}
sqlBuilder.append(field.getName()).append(", ");
sqlBuilder.append(toColumnName(field.getName())).append(", ");
Comment on lines 43 to +49

Choose a reason for hiding this comment

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

문제: save() 메서드에서 "id" 필드를 필터링할 때 field.getName()toColumnName(field.getName())이 일관되지 않게 비교되고 있습니다.

맥락: toColumnName()은 camelCase를 snake_case로 변환하는데, "id"는 변환되지 않으므로 현재 작동하지만, 향후 혼동을 방지하기 위해 다음과 같이 수정하세요:

if ("id".equals(field.getName())) {
    continue;
}

camelCase 필드명으로 비교하는 게 논리적으로 더 명확합니다.

placeholder.append("?, ");
insertFields.add(field);
}
Expand Down Expand Up @@ -128,7 +128,7 @@ public List<T> findAll() {
}

public List<T> findByColumn(String columnName, Object value) {
String sql = "SELECT * FROM " + tableName + " WHERE " + columnName + " = ?";
String sql = "SELECT * FROM " + tableName + " WHERE " + toColumnName(columnName) + " = ?";

Comment on lines 128 to 132

Choose a reason for hiding this comment

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

문제: findByColumn()에서 컬럼명 변환 시 인자값 columnName은 이미 camelCase이므로 toColumnName()을 호출해야 하는데, 이 메서드가 호출자(RegisterWithPost)에서 제대로 처리되고 있는지 확인이 필요합니다.

확인 사항: RegisterWithPostvalidateDuplicate() 메서드에서 EMAIL, NICKNAME 상수(camelCase)를 그대로 전달하고 있으므로 여기서 변환이 필요합니다. 현재 구현은 정확하지만, 호출 시 일관성을 위해 명확한 문서화가 필요합니다.

try (Connection conn = connectionManager.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
Expand All @@ -145,7 +145,7 @@ public List<T> findByColumn(String columnName, Object value) {
}

} catch (SQLException e) {
throw new ErrorException("엔티티 조회 중 오류 (column=" + columnName + ", value=" + value + ")", e);
throw new ErrorException("엔티티 조회 중 오류 (column=" + toColumnName(columnName) + ", value=" + value + ")", e);
}
}

Expand All @@ -167,10 +167,10 @@ public void update(T entity) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if ("id".equals(field.getName())) {
if ("id".equals(toColumnName(field.getName()))) {
continue;
}
sql.append(field.getName()).append(" = ?, ");
sql.append(toColumnName(field.getName())).append(" = ?, ");
Comment on lines 167 to +173

Choose a reason for hiding this comment

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

문제: update() 메서드에서도 동일한 불일치가 있습니다. toColumnName(field.getName())으로 비교하는 것은 일관성이 없습니다.

제안: 원본 필드명으로 비교하세요:

if ("id".equals(field.getName())) {
    continue;
}

updateFields.add(field);
}

Expand Down Expand Up @@ -264,7 +264,7 @@ private T mapRow(ResultSet resultSet) {
continue;
}

String columnName = field.getName();
String columnName = toColumnName(field.getName());
field.setAccessible(true);

Class<?> fieldType = field.getType();
Expand Down Expand Up @@ -311,4 +311,9 @@ private String toTableName(Class<?> clazz) {
}
return name;
}

private String toColumnName(String str){
String snake = str.replaceAll("(?<!^)([A-Z])", "_$1");
return snake.toLowerCase();
}
}
7 changes: 7 additions & 0 deletions src/main/java/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ public enum ErrorCode {
HttpStatus.BAD_REQUEST, "400_MISSING_REGISTER_TOKEN", "회원가입에 필요한 토큰이 누락되었습니다."),
LOGIN_FAILED(
HttpStatus.BAD_REQUEST, "400_LOGIN_FAILED", "아이디 혹은 비밀번호가 잘못되었습니다."),
EMAIL_LENGTH_INVALID(HttpStatus.BAD_REQUEST, "400_EMAIL_LENGTH_INVALID", "이메일은 4 ~ 50글자 사이여야합니다."),
NICKNAME_LENGTH_INVALID(HttpStatus.BAD_REQUEST, "400_NICKNAME_LENGTH_INVALID", "닉네임은 4 ~ 12글자 사이여야합니다."),
PASSWORD_LENGTH_INVALID(HttpStatus.BAD_REQUEST, "400_PASSWORD_LENGTH_INVALID", "비밀번호는 4 ~ 16글자 사이여야합니다."),


EMAIL_ALREADY_EXISTS(HttpStatus.CONFLICT, "409_EMAIL_ALREADY_EXISTS", "이미 가입된 Email입니다."),
NICKNAME_ALREADY_EXISTS(HttpStatus.CONFLICT, "409_NICKNAME_ALREADY_EXISTS", "이미 사용중인 닉네임입니다."),

/* Internal Error */
INTERNAL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "500_INTERNAL", "서버 내부 오류가 발생했습니다."),
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/exception/handler/ErrorExceptionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ public boolean support(Throwable e) {
@Override
public void handle(Throwable t, Socket connection) {
ErrorException error = (ErrorException) t;
logger.debug(error.getThrowable().toString());
logger.debug("{} - {}", error.getMessage(), error.getThrowable().toString());
ErrorCode errorCode = error.getErrorCode();
HttpStatus status = errorCode.getStatus();

String body = toJson(errorCode.getCode(), error.getMessage());
String body = toJson(errorCode.getCode(), errorCode.getMessage());
byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8);

StringBuilder sb = new StringBuilder();
Expand Down