9월 04, 2023

StringUtils.equals 사용이유, string equals() 메서드와 다른점

자바에서는 String의 동일 여부를 체크할 때 

String의 equals() 메서드를 쓴다는 사실은 자바를 처음 배운 사람들도 모두 아는 사실일 것이다. 


다만 가끔 코드를 보다 보면 StringUtils.equals(str1, str2) 의 형태로 String의 동일여부를 체크하는 것인데도 String의 equals() 메서드를 사용하지 않고 StringUtils.equals() 를 사용하는 것을 볼 수 있을 것이다. 


이유는 바로 NullPointerException를 방지하는 데 있다고 할 수 있다. 

Null Pointer의 개념을 처음 만든 Tony Hoare의 "10억 달러 짜리 실수" 라는 유명한 발언만 보더라도 얼마나 개발자들이 NullPointerException을 막기 위해서 노력하고 있는지를 알 수 있을 것이다. 


다시 본론으로 돌아가서 


String str1과 String str2를 비교할 때 

str1.equals(str2) 라는 자바 line 중 str1이 null이면 NullPointerException이 발생한다. 반면 str1이 null이 아니고 str2가 null이면 NullPointerException이 발생하지 않는다. 


따라서 null 값이 확실히 아닌 str2를 인자 자리에 가급적 쓰라고 말을 하지만 그러다가 확실하다고 생각했던 str1의 값이 null로 들어오면 바로 NullPointerException이 발생하는 것이다. 


따라서 이러한 위험을 막기 위해 개발자들은 if 문 분기를 통해 null 이 아닐 때만 비교를 하기도 하는데, 또 다른 방법으로는 아예 StringUtils.equals를 사용하는 것이다. 


StringUtils를 사용하기 위해서는 

 import org.apache.commons.lang.StringUtils ; 

를 통해 먼저 import를 해주고 

 StringUtils.equals(str1, str2)  이런 식으로 사용해주면 비교가 가능하다. 


둘 중 하나가 null이어도 Exception을 뱉지 않아 더욱 안전하다고 볼 수 있다. 



9월 04, 2023

Java Logging Levels (log4j - 로그레벨) 총정리

 java debugging을 할 때 꼭 필요한 것이 log 이다. 

특히 운영 서비스에서 무엇인가 오류가 생겼을 때 필히 확인해야 하는 것이 로그라서 로그를 어느 레벨까지 찍을 것 인지에 대한 고민이 생긴다.


로그 레벨은 크게 7단계로 분류된다. 


1. ALL

2. DEBUG

3. INFO

4. WARN

5. ERROR

6. FATAL

7. OFF

1-> 7 로 갈수록 점점 높은 레벨의 로그 단계를 의미한다. 


즉, 

ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF


이 레벨로 분류가 된다고 할 수 있다. 


흔히 우리가 logger.debug, logger.info 이런 식으로 사용하는데 로그 레벨의 차이라고 볼 수 있다. 


log의 경우 

org.apache.log4j library에서 import해서 사용하고 원하는 로그 레벨을 먼저 지정하면 그 상위 로그 레벨이 로그로 출력되는 구조이다. 


그래서 ALL 이 가장 낮은 log level이고 OFF가 가장 높은 log level이라고 할 수 있다. 

ALL로 log level을 세팅하면 ALL 위의 모든 로그가 출력되니 ALL, 

OFF로 log level을 세팅하면 OFF 위의 모든 로그가 출력되니 출력되는 것이 없어 OFF라고 생각하면 이해하기 편하다. 

import org.apache.log4j.*; public class LogClass { private static org.apache.log4j.Logger log = Logger.getLogger(LogClass.class); public static void main(String[] args) { log.setLevel(Level.INFO);// INFO 레벨 위에 있는 메세지를 로그로 출력 log.trace("Log Level 1: TRACE"); log.debug("Log Level 2: DEBUG");
log.info("Log Level 3: INFO"); log.warn("Log Level 4: WARN"); log.error("Log Level 5: ERROR"); log.fatal("Log Level 6: FATAL"); } }

결과값은 어떻게 될까!


log.setLevel(Level.INFO)를 통해

INFO 레벨 위에 있는 (INFO 레벨 포함) 메세지를 로그로 출력하니 info, warn, error, fatal 단계가 출력될 것이다. 결과값은 아래와 같다.


Log Level 3: INFO Log Level 4: WARN Log Level 5: ERROR
Log Level 6: FATAL


적절한 로그 레벨을 설정하여 과도하게 로그가 쌓이지 않고 꼭 필요한 로그들을 적절히 볼 수 있도록 메세지를 적자.


9월 04, 2023

SQL distinct 사용법 (멀티, 단일 칼럼의 중복값 제거하기)

만약 name, department, age라는 세 개의 칼럼을 가지고 있는 worker 테이블이 있다고 가정해보자. 그리고 해당 테이블에 아래와 같은 DB 정보가 존재한다고 가정해보자. 


만약 여기서


 select * from worker;  


이라고 하면 회사의 모든 직원이 출력될 것이다. (위 예시의 경우 총 6명) 


하지만 만약 회사의 동명이인을 제외한 unique 한 이름을 가진 사람이 몇 명 있는지 알고 싶다면 어떻게 하면 될까? 


바로 

 SELECT DISTINCT name FROM worker; 


이렇게 작성되면 될 것이다.


이렇게 하고 실행을 시켜보면 James, Kate, John 이렇게 세 명의 unique한 이름이 출력 될 것이다. 

하지만 만약 관리자가 팀 구분자까지 하여 unique한 이름을 알고 싶다고 하면 어떻게 하면 될까? 즉 A 팀과 B 팀에 동명이인이 있더라도 이것은 다른 한 쌍으로 간주하겠다는 것이다.


 SELECT DISTINCT name, department FROM worker;  


이런 식으로 작성하면 된다. 


즉, "DISTINCT"가 name과 department에 모두 걸려있다는 것이다. 


위 sql을 실행시켜보면 name과 department가 모두 중복되는 건은 Kate와 Management밖에 없으니 한 건의 중복만 삭제되어 총 5건이 출력 될 것이다. 

만약 중복이 제거된 workers의 개수를 알고 싶다면 distinct 위에 count를 씌울 수도 있다. 


 SELECT COUNT(DISTINCT(name)) FROM worker ; 


이렇게 하면 직접 우리가 결과 값을 count 하지 않고도 3이라는 숫자가 바로 출력되어 unique한 이름이 몇 개인지 알 수 있다. 






9월 03, 2023

Spring 기초 - API 방식이란? 개념 + 예제

오늘은 Spring에서 사용하고 있는 API 방식에 대해 포스팅 해보려고 한다.





먼저 URL이 전달이 되면 API 방식에서는 Spring Controller의 @ResponseBody라는 것을 사용한다.  (위 그림 참고)

이후에 MVC 방식에서 viewResolver를 사용하였는데 API 방식에서는 HttpMessageConverter를 사용한다. 

@ResponseBody로 return값을 받으면 
기본 문자처리를 할 때는 StringHttpMessageConverter가 작동을 하고, 
객체 처리를 할 시에는 MappingJackson2HttpMessageConverter이 동작을 한다. 

예시로 TestController.java 파일을 하나 만들고
TestController.java에는 아래와 같이 적어주면 된다. 


package test.testspring.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {

     static class Test {
        private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
@GetMapping("test-string")
@ResponseBody
public String testString(@RequestParam("name")String name){

return "test "+name;
}

@GetMapping("test-api")
@ResponseBody
public Test testApi(@RequestParam("name")String name){
Test test=
new Test();
test.setName(name);
return test;
}

}

이런 식으로 작성해주면 

http://localhost:8080/test-spring?name=spring

이라고 url이 오면 name 인자에 spring이 들어가서 

test spring이 html 로 반환되는 것이다. 이것은 string으로 반환이 되었으니 StringConverter를 통해 웹브라우저로 반환이 되었을 것이다. 

반면, 

url이
http://localhost:8080/test-api?name=spring
로 들어오게 되면 
@GetMapping("test-api")
이것과 연결되어 test 라는 객체가 반환이 된다. 

여기서 test 라는 객체의 setName 메서드를 통해 name attribute가 spring으로 지정이 된 것이고 이번에는 객체이므로 JsonConverter를 통해 

{name: spring}이라는 형태로 json이 반환되는 것을 알 수 있다.