Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6f6318f
완성 커밋: 2025.10.09
Woojae-Jeong Oct 8, 2025
ed436d5
refactor: TravelRecord의 필드로 totalTravelTime 추가
Woojae-Jeong Oct 8, 2025
3d22d75
hot fix!!!!
euichan2322 Oct 9, 2025
e461c23
refactor:PAUSE 상태에서 current-position 요청 모두 무시
Woojae-Jeong Oct 9, 2025
0c012ff
chore: 실시간 길안내 endPoint 수정
Woojae-Jeong Oct 9, 2025
a4a3757
refactor: 길 안내 기능에서 현 상태에 허락되지 않은 이벤트 발생 시 찍히는 로그 수정
Woojae-Jeong Oct 22, 2025
5f0fea1
refactor: 애플 로그인 토큰 발급 완료 후 로그 추가
Woojae-Jeong Oct 22, 2025
416eddf
refactor: UserIdResolver 에서 토큰 추출 시 로그 추가
Woojae-Jeong Oct 22, 2025
493b9d7
refactor: 로그아웃 여부 확인 기능에 로그 추가
Woojae-Jeong Oct 22, 2025
e74756a
refactor: 웹소켓 메세지 전송 성공 시 로그 추가
Woojae-Jeong Oct 22, 2025
c5fd488
refactor: 웹소켓 응답 DTO 오타 수정
Woojae-Jeong Oct 22, 2025
437839a
feat: keep-alive 이벤트 정의
Woojae-Jeong Oct 25, 2025
fc873e6
feat: keep-alive 이벤트 Req dto정의
Woojae-Jeong Oct 25, 2025
0895a3c
refactor: pause 상태에서의 허락 이벤트 정책 수정
Woojae-Jeong Oct 25, 2025
252a187
feat: keepAlive 이벤트 처리 로직 구현
Woojae-Jeong Oct 25, 2025
4f877c8
feat: KeepAlive 이벤트 응답 dto Mapper 구현
Woojae-Jeong Oct 25, 2025
001f1fb
feat: 월의 시작일과 마지막일에 해당하는 LocalDateTime을 반환하는 기능 구현
Woojae-Jeong Oct 25, 2025
b3534e9
feat: 월 단위 등산 기록 조회 응답 DTO 구현
Woojae-Jeong Oct 25, 2025
632698d
feat: TravelRecord DTO 구현
Woojae-Jeong Oct 25, 2025
ae369f0
feat: 월 단위 산행 기록 조회 기능 구현
Woojae-Jeong Oct 25, 2025
f40b6b0
feat: 산행 기록 조회 API 구현
Woojae-Jeong Oct 25, 2025
067efca
refactor: 산행 기록 저장 책임 분리 (TravelService -> TravelRecordService)
Woojae-Jeong Oct 25, 2025
b781fa9
refactor: 읽기 전용 어노테이션 추가
Woojae-Jeong Oct 25, 2025
1697e7b
feat: 산행 기록 상세 조회 DTO 구현
Woojae-Jeong Oct 25, 2025
cf561cb
refactor: TravelRecord에 displayName 필드 추가
Woojae-Jeong Oct 25, 2025
4dfc6f5
refactor: 산행 기록 메소드명 수정 및 산행 기록 저장 시 displayName을 저장하도록 수정
Woojae-Jeong Oct 25, 2025
dd1801a
feat: TravelRecordNotFoundException 구현
Woojae-Jeong Oct 25, 2025
5e1951d
feat: 산행 기록 상세조회 기능 구현
Woojae-Jeong Oct 25, 2025
cb9f18f
feat: 산행 기록 상세조회 응답 DTO 구현
Woojae-Jeong Oct 25, 2025
3ed6105
feat: 산행 기록 상세조회 API 구현
Woojae-Jeong Oct 25, 2025
483b54e
refactor: TravelRecord BaseUrl 수정
Woojae-Jeong Oct 25, 2025
ba5c377
feat: 산행 기록 삭제 기능 구현
Woojae-Jeong Oct 25, 2025
15f70d7
feat: 산행 기록 삭제 API 구현
Woojae-Jeong Oct 25, 2025
6a19fe5
feat: 산행 기록 controller 로그 추가
Woojae-Jeong Oct 25, 2025
d7f93a2
chore: jpa yml 수정 (DB 시간대 한국으로 설정)
Woojae-Jeong Oct 25, 2025
e90b05d
chore: jpa yml 수정 (sql 로그 끄기)
Woojae-Jeong Oct 25, 2025
dfa5f58
refactor: 산행 기록 조회 JPA 메소드명 수정
Woojae-Jeong Oct 25, 2025
807fab1
refactor: 오타 수정
Woojae-Jeong Oct 25, 2025
e36a198
refactor: 오타 수정
Woojae-Jeong Oct 25, 2025
fe37027
refactor: 사용하지 않은 의존성 제거
Woojae-Jeong Oct 25, 2025
7838c53
style: 오타 수정
Woojae-Jeong Oct 25, 2025
7b8023f
refactor: 웹소켓 세션이 닫혔을 때 TravelTrackingInfo가 남아있으면 삭제되도록 리팩터링
Woojae-Jeong Oct 25, 2025
6304125
Merge branch 'dev' into test
Woojae-Jeong Mar 14, 2026
3cc1970
refactor: 토큰 관련 로그 삭제
Woojae-Jeong Mar 14, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/trigger-cicd.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Trigger Infra CICD
on:
push:
branches: [ dev ]
branches: [ test ]
workflow_dispatch:

jobs:
Expand All @@ -19,4 +19,4 @@ jobs:
"sha": "${{ github.sha }}",
"repoName": "${{ github.repository }}",
"branchName": "${{ github.ref_name }}"
}
}
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ dependencies {
//CoolSMS
implementation 'com.solapi:sdk:1.0.3'

implementation 'org.hibernate.orm:hibernate-core:6.5.2.Final'
implementation 'org.hibernate.orm:hibernate-spatial:6.5.2.Final'

implementation 'org.locationtech.jts:jts-core:1.19.0'

//AWS S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.econo_4factorial.newproject.auth.jwt.repository.BlacklistTokenRepository;
import com.econo_4factorial.newproject.auth.jwt.repository.RefreshTokenRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -17,6 +18,7 @@
import java.util.Date;

@Component
@Slf4j
@RequiredArgsConstructor
public class AuthTokenService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.locationtech.jts.geom.Point;

import java.math.BigDecimal;

Expand All @@ -13,7 +14,7 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(
uniqueConstraints = {
@UniqueConstraint(columnNames = {"latitude", "longitude"})
@UniqueConstraint(columnNames = {"geo_point"})
}
)
public class Base {
Expand All @@ -32,11 +33,8 @@ public class Base {

private Double temperature;

@Column(precision = 16, scale = 14, nullable = false)
private BigDecimal latitude;

@Column(precision = 17, scale = 14, nullable = false)
private BigDecimal longitude;
@Column(name = "geo_point", columnDefinition = "POINT SRID 4326", nullable = false)
private Point GeoPoint;

@Column(nullable = false)
private Long altitude;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.econo_4factorial.newproject.base.domain.Base;
import org.locationtech.jts.geom.Coordinate;

import java.math.BigDecimal;
import java.util.List;
Expand All @@ -13,10 +14,14 @@ public record BaseDTO(

) {
public static BaseDTO from(Base base) {
Coordinate coordinate = base.getGeoPoint().getCoordinate();
return new BaseDTO(
base.getId(),
base.getName(),
List.of(base.getLongitude(), base.getLatitude())
List.of(
BigDecimal.valueOf(coordinate.getX()),
BigDecimal.valueOf(coordinate.getY())
)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public void updateAllBaseWeather() {
List<Base> bases = baseRepository.findAll();
for (Base base : bases) {
try {
BigDecimal lat = base.getLatitude();
BigDecimal lon = base.getLongitude();
BigDecimal lat = BigDecimal.valueOf(base.getGeoPoint().getCoordinate().getY());
BigDecimal lon = BigDecimal.valueOf(base.getGeoPoint().getCoordinate().getX());
Long altitude = base.getAltitude();

Optional<WeatherRes> weatherOpt = Optional.ofNullable(weatherFeignClient.getWeather(lat, lon, apiKey));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.econo_4factorial.newproject.auth.jwt.service.JwtTokenProvider;
import com.econo_4factorial.newproject.common.annotation.UserId;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
Expand All @@ -14,6 +15,7 @@

import java.util.Optional;

@Slf4j
@Component
@RequiredArgsConstructor
public class UserIdResolver implements HandlerMethodArgumentResolver {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.econo_4factorial.newproject.common.config;

import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.PrecisionModel;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GeometryConfig {

@Bean
public GeometryFactory geometryFactory() {
return new GeometryFactory(new PrecisionModel(), 4326);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class WebSocketConfig implements WebSocketConfigurer {

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/travel")
registry.addHandler(webSocketHandler, "/travel-navigate")
.setAllowedOrigins("*");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public enum CommonErrorType implements ErrorType{
ILLEGAL_ARGUMENT_EXCEPTION("COMMON400_002", HttpStatus.BAD_REQUEST, "Illegal argument exception 발생 "),
MISSING_PATH_VARIABLE_EXCEPTION("COMMON400_003", HttpStatus.BAD_REQUEST, "경로 변수(PathVariable)가 누락됐습니다."),
MISSING_REQUEST_PARAM_EXCEPTION("COMMON400_004", HttpStatus.BAD_REQUEST, "쿼리 스트링이 누락됐습니다."),

WEB_SOCKET_IO_EXCEPTION("WEB_SOCKET500", HttpStatus.BAD_REQUEST, "소켓 IOException 발생"),

UN_EXPECTED_EXCEPTION("COMMON500_001", HttpStatus.INTERNAL_SERVER_ERROR, "예기치 못한 에러가 발생했습니다."),
REDIS_NOT_READY_EXCEPTION("COMMON500_002", HttpStatus.INTERNAL_SERVER_ERROR, "Redis의 연결 상태가 Ready가 아닙니다");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.econo_4factorial.newproject.common.exception;

public class WebSocketIOException extends InternalServerException{
public WebSocketIOException(){
super(CommonErrorType.WEB_SOCKET_IO_EXCEPTION);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.econo_4factorial.newproject.common.util;

import lombok.experimental.UtilityClass;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;

@UtilityClass
public class DateUtil {
private static int FIRST_DAY = 1;
public static LocalDateTime getStartOfYearAndMonth (Integer year, Integer month) {
YearMonth yearMonth = YearMonth.of(year, month);
return yearMonth.atDay(FIRST_DAY).atStartOfDay();
}

public static LocalDateTime getEndOfYearAndMonth (Integer year, Integer month) {
YearMonth yearMonth = YearMonth.of(year, month);
return yearMonth.atEndOfMonth().atTime(LocalTime.MAX);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.econo_4factorial.newproject.common.util;

import lombok.experimental.UtilityClass;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

@UtilityClass
public class TimeMapper {

public static LocalDateTime toLocalTime(Long time) {
return Instant.ofEpochMilli(time)
.atZone(ZoneId.of("Asia/Seoul"))
.toLocalDateTime();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.econo_4factorial.newproject.common.util.webSocket;

import com.econo_4factorial.newproject.auth.jwt.service.AuthTokenService;
import com.econo_4factorial.newproject.auth.jwt.service.JwtTokenProvider;
import com.econo_4factorial.newproject.common.exception.WebSocketIOException;
import com.econo_4factorial.newproject.travel.dto.Payload;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
@RequiredArgsConstructor
public class WebSocketAuthService {
private final String AUTHORIZATION = "authorization";

private static final ConcurrentHashMap<String, Long> authenticatedClients = new ConcurrentHashMap<>();

private final AuthTokenService authTokenService;
private final JwtTokenProvider jwtTokenProvider;

public void handleAuthEvent(Payload payload, WebSocketSession session) throws IOException {
Long userId = authenticateUser(payload.data());
updateToAuthenticated(session.getId(), userId);
log.info("사용자 인증 성공. sessionId = {}, userId = {}", session.getId(), userId);
}

private Long authenticateUser(Map<String, Object> data) throws IOException {
Long userId = jwtTokenProvider.getUserIdFromAccessToken((String) data.get(AUTHORIZATION));
authTokenService.isLoggedIn(userId);
return userId;
}

private void updateToAuthenticated(String sessionId, Long userId) {
authenticatedClients.put(sessionId, userId);
}

public Boolean isAuthenticatedSession(WebSocketSession session) {
return authenticatedClients.containsKey(session.getId());

}

public Long getUserId(String id) {
return authenticatedClients.get(id);
}

public void removeSession(WebSocketSession session) {
authenticatedClients.remove(session.getId());
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.econo_4factorial.newproject.common.util.webSocket;

public record WebSocketFailRes(
String status,
String errorCode,
String message
){
public static WebSocketFailRes fail(String errorCode, String message) {
return new WebSocketFailRes("fail", errorCode, message);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.econo_4factorial.newproject.common.util.webSocket;

import com.econo_4factorial.newproject.common.exception.ErrorType;
import com.econo_4factorial.newproject.travel.TravelEvent;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;

@Slf4j
@Component
@RequiredArgsConstructor
public class WebSocketResponser {
private final ObjectMapper mapper;

public void success(WebSocketSession session, TravelEvent event, Object data) throws IOException {
WebSocketSuccessRes successResponse = WebSocketSuccessRes.ok(event, data);
String successResponseJson = mapper.writeValueAsString(successResponse);
session.sendMessage(new TextMessage(successResponseJson));
log.info("웹소켓 메세지 전송 성공 : session = {}. successResponseJson = {}", session.getId(), successResponseJson);
}

public void fail(WebSocketSession session, ErrorType errorType) throws IOException {
String errorCode = errorType.getErrorCode();
String errorMessage = errorType.getMessage();

WebSocketFailRes failResponse = WebSocketFailRes.fail(errorCode, errorMessage);
String failResponseJson = mapper.writeValueAsString(failResponse);
session.sendMessage(new TextMessage(failResponseJson));
log.info("웹소켓 메세지 전송 성공 : session = {}. failResponse = {}", session.getId(), failResponseJson);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.econo_4factorial.newproject.common.util.webSocket;

import com.econo_4factorial.newproject.travel.TravelEvent;

public record WebSocketSuccessRes (
String event,
Object data,
String status
){
public static WebSocketSuccessRes ok(TravelEvent event, Object data) {
return new WebSocketSuccessRes(event.getName(), data, "success");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.locationtech.jts.geom.LineString;

@Entity
@Getter
Expand All @@ -24,6 +25,10 @@ public class Course {
@JoinColumn(name = "peak_base_id", nullable = false)
private Base peakBase;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "destination_base_id", nullable = false)
private Base destinationBase;

@Column(nullable = false, unique = true)
private String name;

Expand All @@ -41,4 +46,7 @@ public class Course {

@Column(nullable = false)
private String displayName;

@Column(columnDefinition = "LINESTRING SRID 4326")
private LineString coordinates;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.econo_4factorial.newproject.course.dto;

import org.locationtech.jts.geom.Coordinate;

public record ClosestCoordinateInfo(
Coordinate coordinate,
Integer index
)
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
public enum CourseErrorType implements ErrorType {
COURSE_NOT_FOUND_EXCEPTION ("COURSE404_001", HttpStatus.NOT_FOUND, "코스를 찾을 수 없습니다"),

BOOKMARK_NOT_FOUND_EXCEPTION ("BOOKMARK404_001", HttpStatus.NOT_FOUND, "코스 북마크를 찾을 수 없습니다");
BOOKMARK_NOT_FOUND_EXCEPTION ("BOOKMARK404_001", HttpStatus.NOT_FOUND, "코스 북마크를 찾을 수 없습니다"),

CLOSEST_COORDINATE_NOT_FOUND_EXCEPTION("TRAVEL500_001", HttpStatus.INTERNAL_SERVER_ERROR, "유저 좌표와 가장 가까운 pathway좌표가 없습니다");

private final String errorCode;
private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.econo_4factorial.newproject.course.exception.InternalServerException;

import com.econo_4factorial.newproject.common.exception.InternalServerException;
import com.econo_4factorial.newproject.course.exception.CourseErrorType;

public class ClosestCoordinateNotFoundException extends InternalServerException {
public ClosestCoordinateNotFoundException() {
super(CourseErrorType.CLOSEST_COORDINATE_NOT_FOUND_EXCEPTION);
}
}
Loading
Loading