Skip to content

Commit

Permalink
Merge branch 'feat/fe/모바일-뷰를-구현한다-' of https://github.com/woowacourse…
Browse files Browse the repository at this point in the history
…-teams/2023-team-by-team into feat/fe/모바일-뷰를-구현한다-
  • Loading branch information
hafnium1923 committed Nov 6, 2023
2 parents 9dd0bcb + 2b1e055 commit 8d76d3e
Show file tree
Hide file tree
Showing 41 changed files with 189 additions and 275 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import team.teamby.teambyteam.filesystem.util.FileUtil;
import team.teamby.teambyteam.member.configuration.dto.MemberEmailDto;
import team.teamby.teambyteam.member.domain.IdOnly;
import team.teamby.teambyteam.member.domain.Member;
import team.teamby.teambyteam.member.domain.MemberRepository;
import team.teamby.teambyteam.member.domain.MemberTeamPlace;
import team.teamby.teambyteam.member.domain.MemberTeamPlaceRepository;
Expand All @@ -48,8 +47,6 @@
import java.util.UUID;
import java.util.stream.Collectors;

import static team.teamby.teambyteam.feed.application.event.FeedEvent.Status.WRITE;

@Slf4j
@Service
@Transactional
Expand Down Expand Up @@ -86,24 +83,24 @@ public Long write(
final Long teamPlaceId
) {
final String content = feedThreadWritingRequest.content();
List<MultipartFile> images = feedThreadWritingRequest.images();
final List<MultipartFile> images = feedThreadWritingRequest.images();
validateEmptyRequest(content, images);
validateImages(images);

final Content contentVo = new Content(content);
final Member member = memberRepository.findByEmail(new Email(memberEmailDto.email()))
.orElseThrow(() -> new MemberException.MemberNotFoundException(memberEmailDto.email()));
final FeedThread feedThread = new FeedThread(teamPlaceId, contentVo, member.getId());
final FeedThread savedFeedThread = feedRepository.save(feedThread);
final Long memberId = memberRepository.findIdByEmail(new Email(memberEmailDto.email()))
.orElseThrow(() -> new MemberException.MemberNotFoundException(memberEmailDto.email()))
.id();

final FeedThread savedFeedThread = feedRepository.save(new FeedThread(teamPlaceId, new Content(content), memberId));
saveImages(images, savedFeedThread);
Long threadId = savedFeedThread.getId();

final Long threadId = savedFeedThread.getId();
log.info("스레드 생성 - 생성자 이메일 : {}, 스레드 아이디 : {}", memberEmailDto.email(), threadId);

// TODO : 캐시 고치고 추가
// feedCache.addCache(teamPlaceId, FeedCache.from(savedFeedThread));

applicationEventPublisher.publishEvent(FeedEvent.of(teamPlaceId, savedFeedThread.getId(), WRITE));
sendFeedWritingEvent(memberEmailDto, teamPlaceId, memberId, savedFeedThread);

return threadId;
}
Expand Down Expand Up @@ -146,6 +143,19 @@ private void saveImages(final List<MultipartFile> images, final FeedThread saved
});
}

private void sendFeedWritingEvent(
final MemberEmailDto memberEmailDto,
final Long teamPlaceId,
final Long memberId,
final FeedThread savedFeedThread
) {
final MemberTeamPlace threadAuthorInfo = memberTeamPlaceRepository.findByTeamPlaceIdAndMemberId(teamPlaceId, memberId)
.orElseThrow(() -> new IllegalArgumentException(String.format("멤버-팀플레이스 조회 실패 memberId : %d, teamPlaceId %d", memberId, teamPlaceId)));
applicationEventPublisher.publishEvent(new FeedEvent(teamPlaceId,
FeedResponse.from(savedFeedThread, threadAuthorInfo, mapToFeedImageResponse(savedFeedThread), memberEmailDto.email())
));
}

@Transactional(readOnly = true)
public FeedsResponse firstRead(final Long teamPlaceId, final MemberEmailDto memberEmailDto, final Integer size) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package team.teamby.teambyteam.feed.application.dto;

import team.teamby.teambyteam.feed.domain.cache.RecentFeedCache;
import team.teamby.teambyteam.feed.domain.cache.RecentFeedCache.FeedImageCache;

public record FeedImageResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ public static FeedResponse from(final Feed feed, final String authorName, final
);
}

public static FeedResponse from(final Feed feed, final MemberTeamPlace threadAuthorInfo, final List<FeedImageResponse> images, final String loginMemberEmail) {
public static FeedResponse from(
final Feed feed,
final MemberTeamPlace threadAuthorInfo,
final List<FeedImageResponse> images,
final String loginMemberEmail
) {
final String createdAt = feed.getCreatedAt().format(DATE_TIME_FORMATTER);

return new FeedResponse(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
package team.teamby.teambyteam.feed.application.event;

import team.teamby.teambyteam.feed.application.dto.FeedResponse;
import team.teamby.teambyteam.sse.domain.TeamPlaceEmitterId;
import team.teamby.teambyteam.sse.domain.TeamPlaceSseEvent;

public class FeedEvent implements TeamPlaceSseEvent {

private static final String EVENT_NAME = "new_thread";

private final FeedSse feedSse;
private final FeedResponse feedResponse;
private final Long teamPlaceId;

private FeedEvent(final FeedSse feedSse) {
this.feedSse = feedSse;
}

public static FeedEvent of(final Long teamPlaceId, final Long feedId, final Status status) {
return new FeedEvent(new FeedSse(teamPlaceId, feedId, status));
public FeedEvent(final Long teamPlaceId, final FeedResponse feedResponse) {
this.teamPlaceId = teamPlaceId;
this.feedResponse = feedResponse;
}

@Override
public Long getTeamPlaceId() {
return feedSse.teamPlaceId;
return teamPlaceId;
}

@Override
Expand All @@ -27,14 +27,30 @@ public String getEventName() {
}

@Override
public Object getEvent() {
return feedSse;
}

public record FeedSse(Long teamPlaceId, Long ThreadId, Status status) {
}

public enum Status {
WRITE
public Object getEvent(final TeamPlaceEmitterId emitterId) {
if (emitterId.isMemberId(feedResponse.authorId())) {
return new FeedResponse(
feedResponse.id(),
feedResponse.type(),
feedResponse.authorId(),
feedResponse.authorName(),
feedResponse.profileImageUrl(),
feedResponse.createdAt(),
feedResponse.content(),
feedResponse.images(),
true
);
}
return new FeedResponse(
feedResponse.id(),
feedResponse.type(),
feedResponse.authorId(),
feedResponse.authorName(),
feedResponse.profileImageUrl(),
feedResponse.createdAt(),
feedResponse.content(),
feedResponse.images(),
false
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,7 @@ public SseEmitter subscribe(final Long teamPlaceId, final MemberEmailDto memberE
final TeamPlaceEventId dummyEventId = TeamPlaceEventId.of(teamPlaceId, dummyEvent.getEventName());
emitter.sendEvent(dummyEventId, dummyEvent);

if (!Objects.isNull(lastEventId) && !lastEventId.isBlank()) {
sendCachedEvents(emitter, teamPlaceId, TeamPlaceEventId.from(lastEventId));
}

log.info("SSE 연결 생성 {}", emitterId);
return emitter.getSingleEmitter();
}

private void sendCachedEvents(
final SseEmitters emitter,
final Long teamPlaceId,
final TeamPlaceEventId lastEventId
) {
final Map<TeamPlaceEventId, Object> events = teamplaceEmitterRepository.findAllEventCacheWithId(teamPlaceId);
events.entrySet().stream()
.filter(entry -> entry.getKey().isPublishedAfter(lastEventId))
.sorted(EVENT_ID_TIME_COMPARATOR)
.forEach(entry -> emitter.sendEvent(entry.getKey(), entry.getValue()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,5 @@ public void publishEvent(final TeamPlaceSseEvent teamPlaceSseEvent) {

final SseEmitters emitters = teamPlaceEmitterRepository.findByTeamPlaceId(targetTeamPlaceId);
emitters.sendEvent(eventId, teamPlaceSseEvent);
teamPlaceEmitterRepository.addEventCache(eventId, teamPlaceSseEvent.getEvent());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
public class SseEmitters {

private static final ObjectMapper objectMapper = new ObjectMapper();
private static final ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
private static final int NUMBER_OF_THREAD = 100;
private static final ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(NUMBER_OF_THREAD);

private final Map<TeamPlaceEmitterId, SseEmitter> emitters;

Expand All @@ -23,12 +24,8 @@ public SseEmitters(final Map<TeamPlaceEmitterId, SseEmitter> emitters) {
}

public void sendEvent(final TeamPlaceEventId eventId, final TeamPlaceSseEvent event) {
sendEvent(eventId, event.getEvent());
}

public void sendEvent(final TeamPlaceEventId eventId, final Object event) {
emitters.forEach(
(emitterId, emitter) -> threadPoolExecutor.execute(() -> sendToEmitter(eventId, event, emitterId, emitter))
(emitterId, emitter) -> threadPoolExecutor.execute(() -> sendToEmitter(eventId, event.getEvent(emitterId), emitterId, emitter))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public String getEventName() {
}

@Override
public Object getEvent() {
public Object getEvent(final TeamPlaceEmitterId emitterId) {
return event;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public boolean isTeamPlaceId(final Long teamPlaceId) {
return Objects.equals(this.teamPlaceId, teamPlaceId);
}

public boolean isMemberId(final Long memberId) {
return Objects.equals(this.memberId, memberId);
}

@Override
public String toString() {
return teamPlaceId + DELIMITER + memberId + DELIMITER + timeStamp;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package team.teamby.teambyteam.sse.domain;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
Expand All @@ -17,7 +15,6 @@ public class TeamPlaceEmitterRepository {
private static final int CACHE_REMOVE_MINUTE = 1;

private final Map<TeamPlaceEmitterId, SseEmitter> emitters = new ConcurrentHashMap<>();
private final EventCache eventCache = new EventCache();

public SseEmitters save(final TeamPlaceEmitterId emitterId, final SseEmitter sseEmitter) {
emitters.put(emitterId, sseEmitter);
Expand Down Expand Up @@ -52,43 +49,4 @@ public SseEmitters findByTeamPlaceId(final Long teamPlaceId) {
public void deleteById(final TeamPlaceEmitterId id) {
emitters.remove(id);
}

public Map<TeamPlaceEventId, Object> findAllEventCacheWithId(final Long teamPlaceId) {
return eventCache.findAllByTeamPlaceId(teamPlaceId);
}

public void addEventCache(final TeamPlaceEventId teamPlaceEventId, final Object event) {
eventCache.put(teamPlaceEventId, event);
}

@Scheduled(fixedDelayString = "${sse.cache-schedule-period}")
private void cacheRefreshSchedule() {
eventCache.clearCacheFor(CACHE_REMOVE_MINUTE);
}

private static class EventCache {

private final Map<TeamPlaceEventId, Object> cache = new ConcurrentHashMap<>();

public Map<TeamPlaceEventId, Object> findAllByTeamPlaceId(final Long teamPlaceId) {
return cache.entrySet()
.stream()
.filter(entry -> entry.getKey().isPublishedTo(teamPlaceId))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

public void put(final TeamPlaceEventId teamPlaceEventId, final Object event) {
cache.put(teamPlaceEventId, event);
}

public void clearCacheFor(final int minutes) {
final LocalDateTime now = LocalDateTime.now();
cache.keySet().forEach(key -> {
if (key.getTimeStamp().isBefore(now.minusMinutes(minutes))) {
cache.remove(key);
}
}
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ public interface TeamPlaceSseEvent {

String getEventName();

Object getEvent();
Object getEvent(TeamPlaceEmitterId emitterId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class SseController {

private static final String NGINX_X_ACCEL_BUFFERING_HEADER = "X-Accel-Buffering";
private static final String NO = "no";
private static final String KEEP_ALIVE = "Keep-Alive";
private static final String TIMEOUT = "timeout=";
private static final int TIMEOUT_VALUE = 5*60;
private static final String CONNECTION = "Connection";
private static final String CONNECTION_KEEP_ALIVE = "keep-alive";

private final SseSubscribeService sseSubscribeService;

Expand All @@ -33,6 +38,8 @@ public ResponseEntity<SseEmitter> connect(
return ResponseEntity
.ok()
.header(NGINX_X_ACCEL_BUFFERING_HEADER, NO)
.header(KEEP_ALIVE, TIMEOUT + TIMEOUT_VALUE)
.header(CONNECTION, CONNECTION_KEEP_ALIVE)
.body(emitter);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package team.teamby.teambyteam.icalendar.acceptance;

import static org.mockito.ArgumentMatchers.any;
import static team.teamby.teambyteam.common.fixtures.acceptance.IcalendarAcceptanceFixtures.GET_ICALENDAR_PUBLISHED_URL;

import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import java.util.concurrent.CountDownLatch;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.assertj.core.api.SoftAssertions;
Expand All @@ -28,6 +24,11 @@
import team.teamby.teambyteam.member.domain.Member;
import team.teamby.teambyteam.teamplace.domain.TeamPlace;

import java.util.concurrent.CountDownLatch;

import static org.mockito.ArgumentMatchers.any;
import static team.teamby.teambyteam.common.fixtures.acceptance.IcalendarAcceptanceFixtures.GET_ICALENDAR_PUBLISHED_URL;

public class IcalendarAcceptanceTest extends AcceptanceTest {

@MockBean
Expand Down Expand Up @@ -56,6 +57,7 @@ public void afterIcalendarCreation() {

public void await() throws InterruptedException {
countDownLatch.await();
Thread.sleep(10);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package team.teamby.teambyteam.sse;

import team.teamby.teambyteam.sse.domain.TeamPlaceEmitterId;
import team.teamby.teambyteam.sse.domain.TeamPlaceSseEvent;

public class TestEvent implements TeamPlaceSseEvent {
Expand All @@ -24,7 +25,7 @@ public String getEventName() {
}

@Override
public Object getEvent() {
public Object getEvent(final TeamPlaceEmitterId emitterId) {
return data;
}

Expand Down
Loading

0 comments on commit 8d76d3e

Please sign in to comment.