프로젝트를 하며 Entity를 설계할 때 멤버변수로 기본 자료형이 아닌 참조형 Wrapper 클래스를 사용하는 것이 좋다는 피드백을 받고 자료형을 수정하였다. Wrapper 클래스란 무엇이고 왜 사용하는지 알아보자

 

Wrapper클래스란 기본 자료형을 객체화 한것이다.

즉 다른 참조형 객체와 동일하게 대입 (=) 할때 값복사가 아닌 주소 복사가 일어난다.

 

 

다음과 같이 기본 자료형에 대응되는 Wrapper class들이 있다.

기본형과 Wrapper 클래스의 구별방법은 앞에 대문자가 있으면 Wrapper클래스이다.

 

Primitive Type Wrapper Class
boolean Boolean
byte Byte
char Character
float Float
int Integer
long Long
short Short
double Double

 

primitive Type사용하면 좋은 점

  • 기본형은 산술 연산이 가능(auto unboxing없이)
  • 성능 측면에서 더 좋다

 

 

Wrapper 클래스 사용하면 좋은 점

  1. 데이터 타입을 Object타입으로 변환할 수 있다. (null값이 가능하다)
  2. java.util패키지의 클래스는 객체만 처리하므로 이 경우도 도움이 된다.
  3. Generic(<>) 은 객체만 저장하게된다. 기본 자료형 사용시 자동 boxingunboxing이 일어난다.
  4. 멀티스레딩에서 동기화를 지원하려면 객체가 필요하다.

 

boxing : 기본자료형을 wrapper클래스로 바꾸어 주는것

unboxing : wrapper클래스를 기본 자료형으로 바꿔주는 것

 

 

 

 

Optional 클래스 사용법

프로젝트를 진행하며 처음으로 Optional이란 클래스를 사용하게 되었다. 사용하면 어떤 것이 좋은지, 어떻게 잘 활용할 수 있는지 알아보고 코드에 적용해보자!!

 

 

Optional 클래스

  • Optional 클래스란 자바 8부터 null값에 대한 처리를 좀 더 깔끔하게 할 수 있도록 추가된 클래스이다. 좀 더 가독성 좋은 코드를 만들기 위해서 사용한다.
  • null 값일 수도 있는 어떤 변수를 Optional 클래스로 감싸주는 방식으로 사용한다.

 

 

사용예시

  1. 유저 객체를 조회해 유저가 있으면 리턴, 없으면 예외를 발생
    • orElseThrow()
      • 객체가 null이면 함수형 인자를 통해 생성된 예외 발생, null이라면 객체를 반환

 

Optional 사용 전

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findById(username);
    if (user == null){
        return user;   
    }else{
        throw new CustomException(ErrorCode.NOT_FOUND_USER);
    }
}

 

Optional 사용

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    Optional<User> user = userRepository.findById(username);
    return user.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER);
}

 

 

  1. 유저를 조회해 유저가 있으면 활동구분코드 검증, 유저가 없으면 예외발생
    • ifPresentOrElse()
      • 함수형 인자 2개를 매개변수로 받는다.
      • 첫번째 인자는 객체에 값이 존재할 경우 실행된다.
      • 두번째 인자는 객체에 값이 null일 경우 실행된다.

 

public User findById(String uid){
    Optional<User> user = userRepository.findById(uid);
    user.ifPresentOrElse(
        // user객체가 있으면 실행
        findUser -> {
            if (findUser.getActDvCd() != '1')
                throw new CustomException(ErrorCode.DELETED_USER);
        },
        // user 객체가 없으면 실행
        () -> {
            throw new CustomException(ErrorCode.NOT_FOUND_USER);
        });
    return user.get();
}

 

  1. 유저를 조회해 유저가 있으면 유저 프로필 이미지 업데이트, 없으면 예외발생
    • isPresent()
      • 객체에 값이 있으면 True, 없으면 false
    • get()
      • Optional 객체 안에 들어있는 값을 리턴
      • 값이 null일 경우 NoSuchElementException이 발생하기 때문에 값이 있는걸 확인하고 사용하는 것이 좋다.

 

public void uploadImage(String uid, String filePath){
Optional<User> user = userRepository.findById(uid);
    // #1
    if (user.isPresent()){
        user.get().updateUserProfileUrl(filePath);
    }else{
        throw new CustomException(ErrorCode.NOT_FOUND_USER);
    }
}

 

public void uploadImage(String uid, String filePath){
Optional<User> user = userRepository.findById(uid);
    // #2
    user.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER))
                .updateUserProfileUrl(filePath);
}

#1#2는 같은 기능의 코드지만 가독성 측면에서 #2형태로 되도록이면 사용하는 것이 좋다.

 

 

사용 시 주의할 점

  • Optional을 필드의 타입으로 사용하지 말자. Optional은 반환 타입을 위해 설계된 타입이다.
  • orElse() 메서드는 객체가 있던 없던 무조건 메서드의 인자가 평가된다. orElseGet() 메서드는 값이 없을 때만 수행된다. 따라서 NULL값 대신 기본값을 리턴하고 싶을 때 객체의 생성비용이 큰 경우라면 orElseGet()을 사용하자.
  • 빈 컬렉션이나 배열을 반환하는 경우는 Optional을 사용하지말자. (그냥 빈 컬렉션이나 배열을 리턴하는 것이 더 좋다.)

 

 

 

 

 

 

 

 

참고

https://www.latera.kr/blog/2019-07-02-effective-optional/

코딩인터뷰 완전분석 (게일 라크만 맥도웰)

 

 

01. 배열과 문자열

문제 1.3

URL화 : 문자열에 들어 있는 모든 공백을 '%20'으로 바꿔 주는 메서드를 작성하라. 최종적으로 모든 문자를 다 담을 수 있을 만큼 충분한 공간이 이미 확보되어 있으며 문자열의 최종 길이가 함께 주어진다고 가정해도 된다.

풀어보기

1. 문자열 순회하면서 이어붙이기

단순하게 문자열을 하나씩 확인하며 공백일 때는 '%20', 공백이 아닐 때는 기존 문자열을 덧붙여서 새로운 결과 문자열을 만들어 리턴한다.

# 1.3 URL화
s = "Mr John Smith"

def toURL(str):
    newS = ""
    for x in s:
        if x == ' ':
            newS += "%20"
        else:
            newS += x
    return newS

print(toURL(s))

# -결과----------------------------------------------------------------------------------
# Mr%20John%20Smith

수정된 풀이(하나의 문자열로 편집하여 리턴)

# 1.3 URL화
s = "Mr John Smith"

def toURL(str):
    n = len(str)

    # 마지막 부터 첫번째까지 순회
    for i in range(len(str)-1, 0-1, -1):
        if str[i] == ' ':
            str = str[:i] + "%20" + str[i+1:]

    return str

print(toURL(s))
# -결과----------------------------------------------------------------------------------
# Mr%20John%20Smith

해답

문제에 정확히 나오지 않았지만 주의해야할 사항은 이 문제는 새로운 문자열을 만들어 변환 하는 것이 아니라, 기존의 문자열을 가지고 조작하여 풀어야 하는 문제!

따라서 문자열을 앞에서 부터 편집하는 것이 아니라, 뒤에서 부터 편집한다. 앞에서 부터 편집하면 아직 편집해야 할 문자가 덮어쓰여질 우려가 있기 때문이다.

 

1. 공백의 갯수를 세고, 문자열을 뒤에서부터 거꾸로 편집하며 복사해나간다. 

void replaceSpaces(char [] str, int trueLength){    // truelength는 주어진 문자열의 길이
    int spaceCount = 0, index, i = 0;
    for (i = 0; i < trueLength; i++){
        if (srt[i] == ' '){
            spaceCount++;        // 공백의 숫자를 센다
        }
    }

    index = trueLength + spaceCount * 2; // 문자열의 길이에다가 URL화로 인해 치환될 문자열의 길이를 더한 값이 index

    if (trueLength < str.length){ // 만약 주어진 문자열의 길이가 실제 문자열의 길이보다 작다면 중간에 종료
        str[trueLength] = '\0'
    }
    // str[trueLength] -> str[index] 로 문자열 복사
    for (i = trueLength - 1; i>=0; i--){
        if (str[i] == ' '){
            str[index - 1] == '0';
            str[index - 2] == '2';
            str[index - 3] == '%';
            index = index-3;
        }
        else{
            str[index - 1] = str[i];
            Index--;
        }
    }
}

The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.

화면을 실행 시 아래와 같은 오류가 발생하는 경우 에러는 해결하는 방법을 알아보자.

원인

이 문제의 원인은 tomcat에서 호출한 경로를 제대로 인식하지 못해서 발생한다고한다.

해결 방법

1. tomcat의 module path를 변경한다.

  • 서버의 톰캣을 더블클릭후 Module 탭으로 들어간다.

  • Web Modules에 Path를 / 로 변경한다

2. 프로젝트 속성 - Web Project Setting에서 Context root를 변경한다.

+ Recent posts