Light Blue Pointer
본문 바로가기
개발일지

2024-01-08, Today I Learned

by 개발바닥곰발바닥!!! 2024. 1. 8.

오늘 고민한 것

엘라스틱서치를 써보는것이 어떨까?

  1. 구체적인 기능과 예시가 있었으면 선뜻 쓸텐데 그렇지 않으면 애매한 거 같다, 쓰는 이유를 정말 구체적으로 만드는게 좋을 거 같다
    1. Q.엘라스틱서치를 쓸만큼의 데이터를 어떻게 구하지 그걸 어떻게 만들지
  2. 검색이나 인덱스 걸기 → 엘라스틱서치 무조건 좋다
  3. 잘쓰고 못쓰고보다 쓰고 안쓰고가 더 중요할 때가 있다 → 면접관한테 이력서를 보여주려면 억지로라도 쓰는게 좋다고 생각한다

프론트 구현 무엇으로 할 건지 : Thymeleaf & Vue & React

  • 처음 배우기 시작 할 때는 ajax/thymeleaf 가 배우기에 빠르긴 하다.
  • Vue 문법이 Thymeleaf 스럽다 ⇒ 참조할 템플릿이 많아 작업에 용이 할 수 있음.
    • ⇒장기적 관점에서의 시간 절약
  • Thymeleaf로 진행을 할 거면 restApi를 지키는 쪽으로 하자.
  • 최대한 시간을 절약 할 수 있는 방안으로 택하자
  • 웹소켓을 열면 Client 라이브러리는 Vue 나 React에 붙이기가 더 쉬울 것

채팅에 Redis 조합

  • 채팅 DB를 Redis로 두었을 때 → 로그아웃이나 컴퓨터를 끄면 날아간다는 소리가 있는데
    • 서비스를 내렸을 때 데이터들이 날아간다
    • 채팅 서비스는 영속적이어야 한다.
      • ⇒ 현 구상대로 짧은 유지기간 후 데이터를 날려버리는 건 아쉽다.
    문제 : 채팅은 구현시 사람들이 채팅방에 입장했을때 내용이 빠르게 조회되길 원하기에 사용하려는 것 ⇒ 영속 저장소로는 절대 쓸 수 없음
    • MongoDB가 괜찮은 방안이 될 수 있다.

 

오늘 알게된 것

1. Spring project의 group 이름 바꾸기

 

 

[Gradle] 기존의 프로젝트의 이름(group, artifact) 바꾸기

때로, 기존 프로젝트를 작업하다가 원본 프로젝트를 그대로 둔 채 이름을 바꾸고 싶을 때가 있다 현재 내가 그런 상황에 있다, ticketing-service보다 performance-ticketing-service로 바꾸고 싶은 것,, 이에

progress0407.tistory.com

group name 변경하기

 

실행 버튼 옆 ...을 눌러서 Application중 group name이 잘못되어 있는것을 삭제해준다

 

 

2. git ignore에 파일 추가

/application-*.yml

이렇게 하면 application-db, application-key 이렇게 써지는거 다 빠짐

 

3. git commit 취소

git rm -r --cached src/main/resources/application-db.yml

 

git reset --soft [commit id]

 

4. Map을 Value기준으로 정렬하는 방법

Map의 entry를 List에 넣고 람다식으로 sorting한다

Map<String, Integer> map = new HashMap<>();
map.put("a", 3);
map.put("b", 2);
map.put("c", 1);

//정렬
List<Map.Entry<String, Integer>> entryList = new LinkedList<>(map.entrySet());
entryList.sort(((o1, o2) -> map.get(o1.getKey()) - map.get(o2.getKey())));

//출력
for(Map.Entry<String, Integer> entry : entryList){
    System.out.println("key : " + entry.getKey() + ", value : " + entry.getValue());
}

 

오늘 한 일

  • Post creation 파트 개발
  • Bean validation 진화시키기

 

메뉴랑도 상관이 있으니까 메뉴 파트도 내가 만듦

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "TB_POST")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String address;

    @Column(nullable = false)
    private String store;

    @Column(nullable = false)
    private Long minPrice;

    @Column(nullable = false)
    private Long deliveryCost;

    @OneToMany(mappedBy = "post")
    private List<Menu> menus;

    @Column
    private Long sumPrice;

    @Column(nullable = false)
    private LocalDateTime deadline;

}

→ sumPrice랑

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "TB_MENU")
public class Menu {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JoinColumn(name = "post_id", nullable = false)
    private Post post;

    @NotBlank
    private String menuname;

    @NotNull
    private Long price;

}
@Getter
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public class BaseTime {

    @CreatedDate
    @Column(updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime modifiedAt;

}

이거에 대한

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "TB_POST")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String address;

    @Column(nullable = false)
    private String store;

    @Column(nullable = false)
    private Long minPrice;

    @Column(nullable = false)
    private Long deliveryCost;

    @OneToMany(mappedBy = "post")
    private List<Menu> menus;

    @Column
    private Long sumPrice;

    @Column
    private LocalDateTime deadline;

}

시간 빼기/ 더하기 부분 구현

 

근데 category 까먹음

category 추가함

package com.moayo.moayoeats.domain.post.entity;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum CategoryEnum {

    BURGER("햄버거"),
    CHICKEN("치킨"),
    PIZZA("피자"),
    KOREAN("한식"),
    SNACK("분식"),
    WESTERN("양식"),
    ASIAN("아시안"),
    JAPANESE("일식"),
    CHINESE("중식"),
    LUNCHBOX("도시락");

    private final String category;

}

에러 출력 어떻게 되는지 보려고 이거저거 해봄

⭐LocalDateTime plus, minus 함수

LocalDateTime은 plus, minus 함수를 제공하며 날짜, 시간을 더하거나 뺄 수 있습니다.

  • plus/minusMonths()
  • plus/minusDays()
  • plus/minusHours()
  • plus/minusMinutes()
  • plus/minusSeconds()
import java.time.LocalDateTime;

public class Example {
    public static void main(String[] args) {

        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);

        LocalDateTime result = now.plusYears(10)
                .plusMonths(10)
                .plusDays(10)
                .plusHours(10)
                .plusMinutes(10)
                .plusSeconds(10);
        System.out.println(result);

        result = now.minusYears(10)
                .minusMonths(10)
                .minusDays(10)
                .minusHours(10)
                .minusMinutes(10)
                .minusSeconds(10);
        System.out.println(result);
    }
}

Output:

2022-12-17T06:17:00.4958389392033-10-27T16:27:10.4958389392012-02-06T20:06:50.495838939

 

LocalDateTime.java 안에 가서 봄!

//-----------------------------------------------------------------------
    /**
     * Returns a copy of this {@code LocalDateTime} with the specified number of hours added.
     * <p>
     * This instance is immutable and unaffected by this method call.
     *
     * @param hours  the hours to add, may be negative
     * @return a {@code LocalDateTime} based on this date-time with the hours added, not null
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public LocalDateTime plusHours(long hours) {
        return plusWithOverflow(date, hours, 0, 0, 0, 1);
    }

    /**
     * Returns a copy of this {@code LocalDateTime} with the specified number of minutes added.
     * <p>
     * This instance is immutable and unaffected by this method call.
     *
     * @param minutes  the minutes to add, may be negative
     * @return a {@code LocalDateTime} based on this date-time with the minutes added, not null
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public LocalDateTime plusMinutes(long minutes) {
        return plusWithOverflow(date, 0, minutes, 0, 0, 1);
    }
public void createPost(PostRequest postReq){
        //set deadline to mins after
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime deadline = now.plusMinutes(postReq.deadline().getMins());

        Post post = Post.builder()
            .address(postReq.address())
            .store(postReq.store())
            .deliveryCost(postReq.deliveryCost())
            .minPrice(postReq.minPrice())
            .deadline(deadline)
            .category(postReq.category())
            .build();

        postRepository.save(post);
    }

이렇게 하니까 잘 된다!

 

Bean validation 진화시키기

AOP처리를 하는데 무슨 에러인지 뽑아서 보여주고 싶음

BindingResult.java
public interface BindingResult extends Errors {

-> public interface getFieldErrors가보니까
/**
	 * Get all errors associated with a field.
	 * @return a List of {@link FieldError} instances
	 * @see #getGlobalErrors()
	 */
	List<FieldError> getFieldErrors();
이게 있음

public class FieldError extends ObjectError{
public FieldError(String objectName, String field, @Nullable Object rejectedValue, boolean bindingFailure,
			@Nullable String[] codes, @Nullable Object[] arguments, @Nullable String defaultMessage) {

	private final String field;

	@Nullable
	private final Object rejectedValue;

	private final boolean bindingFailure;

		super(objectName, codes, arguments, defaultMessage);
		Assert.notNull(field, "Field must not be null");
		this.field = field;
		this.rejectedValue = rejectedValue;
		this.bindingFailure = bindingFailure;
	}

@ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResponse<List<String>> handleMethodArgumentNotValidException(BindingResult bindingResult) {
        List<String> errors = bindingResult.getFieldErrors().stream()
            .map((FieldError fieldError)->
                fieldError.getRejectedValue()+"가 "+
                fieldError.getField()+"의 형식에 맞지 않습니다."
                )
            .toList();
        return new ApiResponse<>(HttpStatus.BAD_REQUEST.value(), "입력값이 잘못되었습니다", errors);
    }
{
    "address":"주소주소",
    "store": "가게가게",
    "minPrice": 20000,
    "deliveryCost":2000,
    "deadline":"M60"
}
{
    "status": 201,
    "message": "글을 생성했습니다.",
    "data": null
}

deadline잘 들어감

 

에러 출력 어떻게 되는지 보려고 이거저거 해봄

{
    "address":"주소주소",
    "store": "",
    "minPrice": 20000,
    "deliveryCost":2000,
    "deadline":"M60"
}
@ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResponse<List<String>> handleMethodArgumentNotValidException(BindingResult bindingResult) {
        List<String> errors = bindingResult.getFieldErrors().stream()
            .map((FieldError fieldError)->
                fieldError.getRejectedValue()+"가 "+
                fieldError.getField()+"의 형식에 맞지 않습니다."
                )
            .toList();
        return new ApiResponse<>(HttpStatus.BAD_REQUEST.value(), "입력값이 잘못되었습니다", errors);
    }

로그에만 이렇게 찍히고 화면에는 출력값이 안 나옴

[Field error in object 'postRequest' on field 'store': rejected value [];

알고보니 ExceptionHandler 클래스 대신 Exception안에서 저러고 있었음

→ ExceptionHandler 클래스 만들어줌

package com.moayo.moayoeats.global.exception;

import com.moayo.moayoeats.global.dto.ApiResponse;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(GlobalException.class)
    public ApiResponse<?> handleGlobalException(GlobalException globalException){
        ErrorCode errorCode = globalException.getErrorCode();
        return new ApiResponse<>(errorCode.getHttpStatus(), errorCode.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResponse<List<String>> handleMethodArgumentNotValidException(
        BindingResult bindingResult) {
        List<String> errors = bindingResult.getFieldErrors().stream()
            .map((FieldError fieldError)->
                fieldError.getField()+"가 "+
                    fieldError.getDefaultMessage()
            )
            .toList();
        return new ApiResponse<>(HttpStatus.BAD_REQUEST.value(), "입력값이 잘못되었습니다", errors);
    }
}
{
    "status": 400,
    "message": "입력값이 잘못되었습니다",
    "data": [
        "store가 must not be blank"
    ]
}

잘 됨!

 

근데 또 저 field랑 defaultmessage는 RuntimeException에도 있나 싶어져서 그거 분류하는것도 만듦

public class RuntimeException extends Exception {
->

public class Exception extends Throwable {
->

public class Throwable implements Serializable {
    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    @java.io.Serial
    private static final long serialVersionUID = -3042686055658047285L;

    /**
     * The JVM saves some indication of the stack backtrace in this slot.
     */
    private transient Object backtrace;

    /**
     * Specific details about the Throwable.  For example, for
     * {@code FileNotFoundException}, this contains the name of
     * the file that could not be found.
     *
     * @serial
     */
    private String detailMessage;

->

이건 나중에 하기!

@ExceptionHandler(RuntimeException.class)
    public ApiResponse<List<String>> handleRuntimeException(
        BindingResult bindingResult) {
        List<String> errors = bindingResult.getFieldErrors().stream()
            .map((FieldError fieldError) -> fieldError.getField() + fieldError.getDefaultMessage())
            .toList();
        return new ApiResponse<>(HttpStatus.BAD_REQUEST.value(), "입력값이 잘못되었습니다", errors);
    }

 

 

 

'개발일지' 카테고리의 다른 글

2023-01-10, Today I Learned  (0) 2024.01.10
2024-01-09, Today I Learned @IdClass Composite key in Spring  (0) 2024.01.09
2024-01-03, Today I Learned  (0) 2024.01.03
2024-01-02, Today I Learned  (0) 2024.01.02
2023-12-28, Today I Learned  (1) 2023.12.28