Skip to content

Commit

Permalink
Merge pull request #783 from woowacourse-teams/develop
Browse files Browse the repository at this point in the history
Hang-Log Prod 2.2.0 Release
  • Loading branch information
mcodnjs authored Jan 23, 2024
2 parents 8fa3188 + abf7d87 commit 891814a
Show file tree
Hide file tree
Showing 2,260 changed files with 62,270 additions and 38,129 deletions.
18 changes: 11 additions & 7 deletions .github/workflows/frontend-e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@ on:
branches:
- develop
paths:
- frontend/**
- .github/**
- frontend-monorepo/packages/hanglog-service/**

defaults:
run:
working-directory: frontend

working-directory: frontend-monorepo/packages/hanglog-service
jobs:
run-dev:
if: contains(github.event.pull_request.labels.*.name, 'FE')
if: contains(github.event.pull_request.labels.*.name, 'FE-Service')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: build design-system
run: yarn run build:design-system

- name: Create env file
run: |
touch .env
Expand All @@ -28,13 +30,15 @@ jobs:
cat .env
- name: Cypress run
uses: cypress-io/github-action@v5
uses: cypress-io/github-action@v6
with:
browser: chrome
start: npm run serve:dev
start: yarn run serve:dev
wait-on: "http://localhost:3000"
record: false
working-directory: ./frontend
working-directory: ./frontend-monorepo/packages/hanglog-service
install-command: yarn install
command: yarn run --binaries-only cypress run
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@

### 프론트엔드

<img width="70%" alt="스크린샷 2023-10-18 오후 5 32 31" src="https://github.com/woowacourse-teams/2023-hang-log/assets/45068522/db073e79-a6cc-4bda-844b-58ac17d95e90">
<img width="80%" alt="스크린샷 2024-01-22 오후 11 43 48" src="https://github.com/woowacourse-teams/2023-hang-log/assets/45068522/9284b40e-c931-4f04-ac78-9c97a548338e">

### 백엔드

<img width="70%" alt="스크린샷 2023-10-18 오후 5 32 13" src="https://github.com/woowacourse-teams/2023-hang-log/assets/45068522/9cb02663-f540-4659-8d35-6fbde5c3cf2d">
<img width="80%" alt="스크린샷 2024-01-22 오후 11 43 16" src="https://github.com/woowacourse-teams/2023-hang-log/assets/45068522/b64dde4f-9467-4427-a361-e2301657efa4">

### 인프라

<img width="70%" alt="스크린샷 2023-10-18 오후 5 32 49" src="https://github.com/woowacourse-teams/2023-hang-log/assets/45068522/15309fe5-bda5-4793-8e65-af52e7b4a4e9">
<img width="80%" alt="스크린샷 2024-01-22 오후 11 44 27" src="https://github.com/woowacourse-teams/2023-hang-log/assets/45068522/d0e03eb4-e159-405b-b22e-3d3fa79b99b7">

<br>

Expand Down
2 changes: 1 addition & 1 deletion backend/backend-submodule
6 changes: 6 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.528'
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

testImplementation 'io.rest-assured:rest-assured:5.3.1'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
Expand All @@ -43,10 +45,14 @@ dependencies {

annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation "org.junit.jupiter:junit-jupiter:5.8.1"
testImplementation "org.testcontainers:testcontainers:1.17.6"
testImplementation "org.testcontainers:junit-jupiter:1.17.6"

implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'

implementation 'com.github.maricn:logback-slack-appender:1.6.1'

implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-mysql'
Expand Down
11 changes: 11 additions & 0 deletions backend/docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,14 @@ services:
- ./db/mysql/data:/var/lib/mysql
- ./db/mysql/config:/etc/mysql/conf.d
- ./db/mysql/init:/docker-entrypoint-initdb.d

redis:
image: redis:alpine
command: redis-server --port 6379
container_name: hanglog_redis
hostname: root
labels:
- "name=redis"
- "mode=standalone"
ports:
- 16379:6379
3 changes: 3 additions & 0 deletions backend/src/docs/asciidoc/docs.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
| 서버 에러
|===

=== Exception codes
[[exception-codes]]
include::{snippets}/exception-code-controller-test/get-exception-codes/exception-response-fields.adoc[]

== 여행 API

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@
import hanglog.community.dto.response.CommunityTripResponse;
import hanglog.community.dto.response.RecommendTripListResponse;
import hanglog.global.exception.BadRequestException;
import hanglog.like.domain.LikeCount;
import hanglog.like.domain.LikeInfo;
import hanglog.like.domain.MemberLike;
import hanglog.like.dto.LikeElement;
import hanglog.like.dto.LikeElements;
import hanglog.like.repository.LikeRepository;
import hanglog.like.domain.repository.LikeCountRepository;
import hanglog.like.domain.repository.LikeRepository;
import hanglog.like.domain.repository.MemberLikeRepository;
import hanglog.trip.domain.Trip;
import hanglog.trip.domain.repository.TripCityRepository;
import hanglog.trip.domain.repository.TripRepository;
Expand All @@ -25,6 +30,7 @@
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
Expand All @@ -45,6 +51,8 @@ public class CommunityService {
private final CityRepository cityRepository;
private final RecommendStrategies recommendStrategies;
private final PublishedTripRepository publishedTripRepository;
private final LikeCountRepository likeCountRepository;
private final MemberLikeRepository memberLikeRepository;

@Transactional(readOnly = true)
public CommunityTripListResponse getCommunityTripsByPage(final Accessor accessor, final Pageable pageable) {
Expand Down Expand Up @@ -119,20 +127,31 @@ public TripDetailResponse getTripDetail(final Accessor accessor, final Long trip
final LocalDateTime publishedDate = publishedTripRepository.findByTripId(tripId)
.orElseThrow(() -> new BadRequestException(NOT_FOUND_TRIP_ID))
.getCreatedAt();
final LikeElements likeElements = new LikeElements(likeRepository.findLikeCountAndIsLikeByTripIds(
accessor.getMemberId(),
List.of(tripId)
));
final Map<Long, LikeInfo> likeInfoByTrip = likeElements.toLikeMap();

final LikeElement likeElement = getLikeElement(accessor.getMemberId(), tripId);
final Boolean isWriter = trip.isWriter(accessor.getMemberId());

return TripDetailResponse.publishedTrip(
trip,
cities,
isWriter,
isLike(likeInfoByTrip, tripId),
getLikeCount(likeInfoByTrip, tripId),
likeElement.isLike(),
likeElement.getLikeCount(),
publishedDate
);
}

private LikeElement getLikeElement(final Long memberId, final Long tripId) {
final Optional<LikeCount> likeCount = likeCountRepository.findById(tripId);
final Optional<MemberLike> memberLike = memberLikeRepository.findById(memberId);
if (likeCount.isPresent() && memberLike.isPresent()) {
final Map<Long, Boolean> tripLikeStatusMap = memberLike.get().getLikeStatusForTrip();
if (tripLikeStatusMap.containsKey(tripId)) {
return new LikeElement(tripId, likeCount.get().getCount(), tripLikeStatusMap.get(tripId));
}
return new LikeElement(tripId, likeCount.get().getCount(), false);
}
return likeRepository.findLikeCountAndIsLikeByTripId(memberId, tripId)
.orElseGet(() -> new LikeElement(tripId, 0, false));
}
}
3 changes: 1 addition & 2 deletions backend/src/main/java/hanglog/expense/domain/Expense.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import hanglog.category.domain.Category;
import hanglog.global.BaseEntity;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
Expand Down Expand Up @@ -37,7 +36,7 @@ public class Expense extends BaseEntity {
@Embedded
private Amount amount;

@ManyToOne(fetch = LAZY, cascade = CascadeType.PERSIST)
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "category_id", nullable = false)
private Category category;

Expand Down
30 changes: 30 additions & 0 deletions backend/src/main/java/hanglog/global/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package hanglog.global.config;

import hanglog.global.decorator.MdcTaskDecorator;
import hanglog.global.exception.AsyncExceptionHandler;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

@Bean
public TaskExecutor taskExecutor() {
final ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(8);
taskExecutor.setTaskDecorator(new MdcTaskDecorator());
taskExecutor.setThreadNamePrefix("async-task-");
return taskExecutor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
23 changes: 23 additions & 0 deletions backend/src/main/java/hanglog/global/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package hanglog.global.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;

@Configuration
@EnableRedisRepositories
public class RedisConfig {

@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package hanglog.global.decorator;

import java.util.Map;
import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;

public class MdcTaskDecorator implements TaskDecorator {

@Override
public Runnable decorate(final Runnable runnable) {
final Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
return () -> {
if (copyOfContextMap != null) {
MDC.setContextMap(copyOfContextMap);
}
runnable.run();
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.lang.reflect.Method;
import lombok.RequiredArgsConstructor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
Expand All @@ -11,6 +12,7 @@
public class ConnectionProxyHandler implements MethodInterceptor {

private static final String JDBC_PREPARE_STATEMENT_METHOD_NAME = "prepareStatement";
private static final String HIKARI_CONNECTION_NAME = "HikariProxyConnection";

private final Object connection;
private final LoggingForm loggingForm;
Expand All @@ -30,7 +32,14 @@ public Object invoke(@Nonnull final MethodInvocation invocation) throws Throwabl
}

private boolean hasPreparedStatementInvoked(final MethodInvocation invocation) {
return invocation.getMethod().getName().equals(JDBC_PREPARE_STATEMENT_METHOD_NAME);
final Object targetObject = invocation.getThis();
if (targetObject == null) {
return false;
}
final Class<?> targetClass = targetObject.getClass();
final Method targetMethod = invocation.getMethod();
return targetClass.getName().contains(HIKARI_CONNECTION_NAME) &&
targetMethod.getName().equals(JDBC_PREPARE_STATEMENT_METHOD_NAME);
}

private boolean hasConnection(final Object result) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package hanglog.global.exception;

import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

@Override
public void handleUncaughtException(final Throwable ex, final Method method, final Object... params) {
log.error(ex.getMessage(), ex);
}
}
9 changes: 9 additions & 0 deletions backend/src/main/java/hanglog/global/filter/MdcKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package hanglog.global.filter;

public enum MdcKey {
REQUEST_ID,
REQUEST_METHOD,
REQUEST_URI,
REQUEST_TIME,
REQUEST_IP;
}
45 changes: 45 additions & 0 deletions backend/src/main/java/hanglog/global/filter/MdcLoggingFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package hanglog.global.filter;

import static hanglog.global.filter.MdcKey.REQUEST_ID;
import static hanglog.global.filter.MdcKey.REQUEST_IP;
import static hanglog.global.filter.MdcKey.REQUEST_METHOD;
import static hanglog.global.filter.MdcKey.REQUEST_TIME;
import static hanglog.global.filter.MdcKey.REQUEST_URI;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.UUID;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MdcLoggingFilter implements Filter {

@Override
public void doFilter(
final ServletRequest request,
final ServletResponse response,
final FilterChain chain
) throws IOException, ServletException {
setMdc((HttpServletRequest) request);
chain.doFilter(request, response);
MDC.clear();
}

private void setMdc(final HttpServletRequest request) {
MDC.put(REQUEST_ID.name(), UUID.randomUUID().toString());
MDC.put(REQUEST_METHOD.name(), request.getMethod());
MDC.put(REQUEST_URI.name(), request.getRequestURI());
MDC.put(REQUEST_TIME.name(), LocalDateTime.now().toString());
MDC.put(REQUEST_IP.name(), request.getRemoteAddr());
}
}
Loading

0 comments on commit 891814a

Please sign in to comment.