-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/#23 Log를 저장할 수 있는 기능 추가 #30
Changes from all commits
b665835
2f9294f
51bfb9e
015737d
626cb2b
9507f46
df7e237
45de29e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package info.logbat.log.domain.enums; | ||
package info.logbat.domain.log.domain.enums; | ||
|
||
public enum Level { | ||
ERROR, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package info.logbat.log.domain.values; | ||
package info.logbat.domain.log.domain.values; | ||
|
||
import lombok.Getter; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package info.logbat.domain.log.repository; | ||
|
||
import info.logbat.domain.log.domain.Log; | ||
import java.sql.PreparedStatement; | ||
import java.sql.Statement; | ||
import java.sql.Timestamp; | ||
import java.util.Optional; | ||
import lombok.AllArgsConstructor; | ||
import org.springframework.dao.EmptyResultDataAccessException; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.core.RowMapper; | ||
import org.springframework.jdbc.support.GeneratedKeyHolder; | ||
import org.springframework.jdbc.support.KeyHolder; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
@AllArgsConstructor | ||
public class LogRepository { | ||
|
||
private final JdbcTemplate jdbcTemplate; | ||
|
||
public long save(Log log) { | ||
String sql = "INSERT INTO logs (application_id, level, log_data, timestamp) VALUES (?, ?, ?, ?)"; | ||
|
||
KeyHolder keyHolder = new GeneratedKeyHolder(); | ||
|
||
jdbcTemplate.update(connection -> { | ||
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); | ||
ps.setLong(1, log.getApplicationId()); | ||
ps.setString(2, log.getLevel().name()); | ||
ps.setString(3, log.getLogData().getValue()); | ||
ps.setTimestamp(4, Timestamp.valueOf(log.getTimestamp())); | ||
return ps; | ||
}, keyHolder); | ||
|
||
return Optional.ofNullable(keyHolder.getKey()) | ||
.map(Number::longValue) | ||
.orElseThrow(() -> new IllegalStateException("로그 저장에 실패했습니다.")); | ||
} | ||
|
||
public Optional<Log> findById(Long logId) { | ||
String sql = "SELECT * FROM logs WHERE log_id = ?"; | ||
|
||
try { | ||
return Optional.ofNullable( | ||
jdbcTemplate.queryForObject( | ||
sql, | ||
LOG_ROW_MAPPER, | ||
logId | ||
)); | ||
} catch (EmptyResultDataAccessException e) { | ||
return Optional.empty(); | ||
} | ||
} | ||
|
||
private static final RowMapper<Log> LOG_ROW_MAPPER = (rs, rowNum) -> new Log( | ||
rs.getLong("log_id"), | ||
rs.getLong("application_id"), | ||
rs.getString("level"), | ||
rs.getString("log_data"), | ||
rs.getTimestamp("timestamp").toLocalDateTime() | ||
); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 실패 케이스도 작성하면 좋을 것 같습니다 :D |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package info.logbat.domain.log.repository; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import info.logbat.domain.log.domain.Log; | ||
import info.logbat.domain.log.domain.enums.Level; | ||
import jakarta.transaction.Transactional; | ||
import java.time.LocalDateTime; | ||
import java.util.Optional; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.test.context.ActiveProfiles; | ||
|
||
@SpringBootTest | ||
@Transactional | ||
@ActiveProfiles("test") | ||
@DisplayName("LogRepository 테스트") | ||
class LogRepositoryTest { | ||
|
||
@Autowired | ||
private LogRepository logRepository; | ||
|
||
@DisplayName("Log를 저장할 수 있다.") | ||
@Test | ||
void saveLog() { | ||
// given | ||
String 로그_레벨 = "INFO"; | ||
String 로그_데이터 = "테스트_로그_데이터"; | ||
LocalDateTime 타임스탬프 = LocalDateTime.of(2021, 1, 1, 0, 0, 0); | ||
|
||
Log 로그 = new Log(1L, 로그_레벨, 로그_데이터, 타임스탬프); | ||
|
||
// when | ||
long 저장된_ID = logRepository.save(로그); | ||
|
||
// then | ||
assertThat(저장된_ID) | ||
.isPositive(); | ||
} | ||
|
||
@DisplayName("저장한 Log를 조회할 수 있다.") | ||
@Test | ||
void findLog() { | ||
// given | ||
String 로그_레벨 = "INFO"; | ||
String 로그_데이터 = "테스트_로그_데이터"; | ||
LocalDateTime 타임스탬프 = LocalDateTime.of(2021, 1, 1, 0, 0, 0); | ||
|
||
long 로그_ID = logRepository.save(new Log(1L, 로그_레벨, 로그_데이터, 타임스탬프)); | ||
|
||
// when | ||
Optional<Log> 저장된_로그 = logRepository.findById(로그_ID); | ||
|
||
// then | ||
assertThat(저장된_로그).isPresent() | ||
.get() | ||
.extracting("logId", "applicationId", "level", "logData.value", "timestamp") | ||
.containsExactly(로그_ID, 1L, Level.INFO, "테스트_로그_데이터", | ||
LocalDateTime.of(2021, 1, 1, 0, 0, 0)); | ||
} | ||
|
||
@DisplayName("없는 Log를 조회하면 빈 Optional을 반환한다.") | ||
@Test | ||
void findNonexistentLog() { | ||
// given | ||
long 없는_로그_ID = 1L; | ||
|
||
// when | ||
Optional<Log> 저장된_로그 = logRepository.findById(없는_로그_ID); | ||
|
||
// then | ||
assertThat(저장된_로그).isEmpty(); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코드 리뷰 결과는 다음과 같습니다:
이러한 점들을 개선하면 코드의 안정성과 가독성을 높이는 데 도움이 될 것입니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코드 리뷰를 시작하겠습니다. 버그 리스크 및 개선 제안
결론전반적으로 잘 구성된 테스트 코드임에도 불구하고 몇 가지 잠재적인 문제점과 개선 사항이 발견되었습니다. 이러한 점들을 고려하여 코드를 보강하면 더 안정적인 테스트 케이스가 될 것입니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드 패치에 대한 간단한 리뷰를 제공하겠습니다.
버그 위험
log.getTimestamp()
가 null일 경우,Timestamp.valueOf(log.getTimestamp())
에서 NullPointerException이 발생할 수 있습니다. 이 부분에 대한 검증이 필요합니다.log.getLevel().name()
이 예상하지 못한 값을 반환할 수 있는지 확인해야 합니다. Enum의 값이 데이터베이스의 값과 다를 경우 문제를 일으킬 수 있습니다.개선 제안
findById
메서드에서Optional.ofNullable
대신jdbcTemplate.queryForObject
의 결과가 null인 경우 외부에서 처리하도록 할 수도 있습니다.이러한 점들에 유의하면 코드의 안정성과 가독성을 높일 수 있을 것입니다.