String, StringBuffer, StringBuilder의 장단점 및 차이점

자바에서 String과 StringBuffer, StringBuilder의 차이점을 알아본다.

앞서 이 클래스들의 공통점은 모두다 String(문자열)을 저장하고 관리하는 클래스들이 라는 것이다.(간단히 참고)

String vs StringBuffer, StringBuilder

String은 immutable(불변)하고 StringBuffer, StringBuilder는 mutable(가변)하다.

다시 말해서, String 클래스는 StringBuffer 클래스나 StringBuilder 클래스와 다르게 리터럴을 통해 생성되면 그 인스턴스의 메모리 공간은 절대 변하지 않는다.

String literalString = "literal"; //리터럴로 생성하는 방식 String newString = new String("literal"); //new로 생성하는 방식 //위에서 "literal" 이라는 문자열을 String Pool에서 생성했기 때문에 이후에 추가한 str1, str2, str3는 추가적으로 생성하지않고 똑같은 문자열을 가리킨다. String str1 = "literal"; String str2 = "literal"; String str3 = "literal";

위 코드의 두 가지 String class 생성 방식에 따라 나뉘는데, 두 방식 모두 JVM 메모리 중 힙(heap) 영역에 생성된다는 점은 같다.

하지만 리터럴로 생성하면 특수하게 "String Pool"이라는 공간에 생성된다. 이 메모리 공간에 생성된 문자열 값은 절대 변하지 않는다는 얘기다.

그래서 '+' 연산이나 .concat() 메소드를 이용해서 문자열 값에 변화를 줘도 메모리 공간 내의 값이 변하는 것이 아니라, "String Pool"이라는 공간 안에 메모리를 할당받아 새로운 String 클래스 객체를 만들어서 문자열을 나타내는 것이다.

이렇게 새로운 문자열이 만들어지면 '기존의 문자열'은 가비지 콜렉터에 의해 제거되야 하는 단점(언제 제거될지 모름)이 있다.

또한 이러한 문자열 연산이 많아진다면, 연산 한 번 할 때마다 계속해서 문자열 객체를 만드는 오버헤드가 발생하므로 성능이 떨어질 수 밖에 없다. (+ 연산에 내부적으로 char배열을 사용함)

대신 String 클래스의 객체는 불변하기 때문에 단순하게 읽어가는 조회연산에서는 타 클래스보다 빠르게 읽을 수 있는 장점이 있다. (추가 장점으로 불변하기 때문에 멀티쓰레드환경에서 동기화를 신경쓸 필요가 없다.)

String 클래스가 적절한 경우

결론적으로, String 클래스는 문자열 연산이 적고, 자주 참조(조회)하는 경우에 사용하면 좋다. (특히 멀티 쓰레드 환경에서 신경쓸 게 없어서 좋다.)

StringBuffer vs StringBuilder ?

StringBuffer와 StringBuilder 클래스는 String과 다르게 mutable(변경가능)하다고 그랬다.

즉 문자열 연산을 할 때, 클래스는 한 번만 만들고(new), 메모리의 값을 변경시켜서 문자열을 변경한다.

그러므로 쓸데없이 중간에 문자열데이터들을 안 만들게 되니까 문자열 연산이 자주 있을 때 사용하면 성능이 좋다!

심지어 StringBuffer와 StringBuilder 클래스의 메서드들이 같으므로 호환(?)이 가능하다.

그렇다면 StringBuffer와 StringBuilder의 차이는 무엇일까?

차이점은 StringBuffer는 멀티쓰레드환경에서 synchronized키워드가 가능하므로 동기화가 가능하다. 즉, thread-safe하다. 반대로 StringBuilder는 thread-safe하지 않다.

StringBuilder는 동기화를 지원하지 않기 때문에 멀티 쓰레드 환경에서는 적합하지 않다.

대신 StringBuilder가 동기화를 고려하지 않기 때문에 싱글쓰레드 환경에서 StringBuffer에 비해 연산처리가 빠른 장점이 있다.

StringBuffer, StringBuilder가 적절한 경우

결론적으로, 문자열 연산이 많을 때 두 클래스를 사용하지만 멀티 쓰레드 환경에서는 StringBuffer를 사용하면 좋고, 싱글 쓰레드 환경이거나 멀티 쓰레드여도 굳이 동기화가 필요없는 경우에는 StringBuilder를 사용하는 것이 좋다.

정리

String 클래스 불변 객체이기 때문에 문자열 연산이 많은 프로그래밍이 필요할 때 계속해서 인스턴스를 생성하므로 성능이 떨어지지만 조회가 많은 환경, 멀티쓰레드 환경에서 성능적으로 유리합니다.

StringBuffer 클래스와 StringBuilder 클래스는 문자열 연산이 자주 발생할 때 문자열이 변경가능한 객체기 때문에 성능적으로 유리합니다.

StringBuffer와 StringBuilder의 차이점은 동기화 지원의 유무이고 동기화를 고려하지 않는 환경에서 StringBuilder가 성능이 더 좋고, 동기화가 필요한 멀티쓰레드 환경에서는 StringBuffer를 사용하는 것이 유리합니다.

추가적인 내용

  • StringBuffer와 StringBuilder는 성능으로 따졌을 때 2배의 속도 차이가 있다고 합니다. 하지만 참고사이트의 속도 차이 실험 결과, append() 연산이 약 1억6천만 번 일어날 때 약 2.6초 정도의 속도차이를 보인다고 합니다. (해당 수치가 유의미할지 무의미할지는 만드시는 프로그램의 스펙에 따라 나뉩니다. 참고하시기 바랍니다.)
    (String은 +연산이 16만 번이상 넘어가게 되면 10초이상 걸리면서 못 쓸정도의 성능을 보입니다.)
    → 따라서 문자열 연산이 많지만 엄청나게 일어나지 않는 환경이라면 StringBuffer를 사용해서 thread-safe한 것(안정적인 것)이 좋다는 게 제 생각입니다.
  • JDK1.5이상부터 String에서 +연산으로 작성하더라도 StringBuilder로 컴파일하게 만들어 놨다지만 여전히 String클래스의 객체 생성하는 부분을 동일하므로 StringBuffer, StringBuilder 사용이 필요합니다. (* 반복문 안에서는 StringBuilder로 컴파일 시켜주지 않는다는 얘기를 들었습니다. (사실확인필요하지만...))
  • StringBuffer, StringBuilder의 경우 buffer size를 초기에 설정해야하는데 이런 생성, 확장 오버로드가 걸려 버퍼사이즈를 잘못 초기화할 경우 성능이 좋지 않을 수 있는 문제가 있습니다.
  • String클래스가 컴파일러 분석 단계에서 최적화될 가능성이 있기때문에 간혹 성능이 잘나오는 경우도 있음. 문자열 연산이 많지 않은 경우는 그냥 사용해도 무방.
  • 런타임에서 문자열 조합이 많아질 경우, String 클래스는 여전히 성능이 아주 안좋기 때문에!
  • , .concat()을 사용하는 사고(?)를 치면 안된다. 특히 현업에서...



출처: https://jeong-pro.tistory.com/85?category=793347 [기본기를 쌓는 정아마추어 코딩블로그]

해시(Hash)와 암호화(Encryption)의 차이점이 무엇인가요?

둘 다 암호화 기법이지만 Hash 단방향 암호화 기법이고 Encryption은 양방향 암호화 기법이다.

쉽게 설명하면 Hash는 평문을 암호화된 문장(텍스트)으로 만들어주는 기능을 하고,

Encryption은 평문을 암호화된 문장(텍스트)로 만들어주는 기능을 하고 + 암호화된 문장을 다시 평문으로 만드는 복호화 기능도 한다.

해시(Hash)는 어떻게 암호화가 이루어질까?

예시로 설명하면 평문의 비밀번호 "jeongpro1234"를 해시함수(해시 알고리즘)를 이용하여 고정된 길이의 암호화된 문자열로 바꿔 버리는 것이 해시를 이용한 암호화 기법이다.

* 해시에서 알아야 할 것들

- 해시 알고리즘 및 밑에서 얘기할 암호화 알고리즘은 종류가 다양하며, 알고리즘은 모두에게(해커에게도) 공개되어있다. 

(대신 알고리즘에 취약점이 발견되어 취약점에 의해 보안이 뚫리면 알고리즘을 만든 사람(암호학자)이 형사 처벌(?)된다. 따라서 알고리즘을 만들 때 엄청나게 많은 검증을 거치게 되고, 많은 개발자들이 검증된 것들을 사용한다.)

- 해시 알고리즘마다 Hash 길이가 다르고 이미 보안이 뚫린 해시 함수가 존재한다.

(MD5, SHA-1, HAS-180은 사용하면 안된다. SHA-256, SHA-512등을 사용하기를 권고함. 참고로 SHA-512가 보안이 더 좋음.)

- 해시 알고리즘은 특정 입력 대해 항상 같은 해시 값을 리턴한다.

(이 점을 이용해서 '인증'이 가능하다. 어떤 입력인지 몰라도 해시함수를 이용해서 해시된 값이 일치하면 입력이 같다는 것이 입증된다.)

- 해시된 값은 입력이 다른 값이지만 같을 수 있다.

입력은 만들어낼 수 있는 평문이 길이제한이 없다면 무한정으로 만들어 낼 수 있지만 해시된 값은 항상 고정된 길이의 값으로 나타내므로 한계가 있기 때문에 다른 입력이지만 해시된 값이 같은 경우가 나타날 수 있음.

(중복이 적게 나타날 수록 좋은 해시함수)

단순히 해시 함수를 이용해서 변환만 한다고 해서 보안이 완벽에 가깝다고 말할 수 없다.

왜냐하면 해커가 무차별적으로 임의의 값을 입력하면서 비밀번호를 알아낼 수 있기 때문이다.

이런 점을 보완하기 위해 비밀번호에 솔트(salt)값을 넣는 방법이 있고, 해시 함수를 여러번 돌리는 방법이 있다.

비밀번호를 예로 들면 개개인마다 다른 임의의 솔트값을 데이터베이스에 같이 저장해서 해시함수를 적용할 때 해당 솔트값을 이용하는 것으로 비밀번호를 암호화 시키는 방법이다.

참고로 솔트값을 넣는 방법은 다양하다. (비밀번호 양쪽에 솔트값을 넣고 해시함수를 수행하는 방법, 한쪽에 넣고 해시함수를 수행한다음 다시 솔트값을 넣고 수행하는 방법등)

따라서 솔트값을 알아도 해커가 운이 정말 좋아도 한 명정도의 개인정보를 알아갈 뿐, 개인정보 유출사태는 일어나지 않게 된다.

그러나 해시 함수는 태생이 암호화를 위해서가 아니라 검색을 빨리하기 위한 것이라 속도가 굉장히 빠르다.

그렇기 때문에 요즘 나오는 고성능의 GPU등을 이용해서 무차별적으로 대입해보기를 시도하면 1초에 약50억번이상 대입이 가능하기 때문에 대입하다보면 뚫을 수도 있다.

따라서 이런 점을 보완해야 하는데 이럴 때 해시 함수를 여러번 돌리면서 강제적으로 1회 대입에 걸리는 시간을 높이는 방법을 이용하는 것이다.

사람은 로그인에 걸리는 시간(대입에 걸리는 시간)이 0.2초가 걸리더라도 느리다고 여기지 않는다.

이 점을 이용하여 1회에 약 0.2초가 걸리게 강제적으로 만든다면 무차별 대입을 50억번하려면... 어마어마한 시간이 걸리기 때문에 해커들이 시도조차 하기 어렵게 만든다.

결국 좋은 해시 함수와 솔트를 이용하는 방법 + 해시 함수를 여러번 적용하는 방법을 이용하는 key derivation function가 존재한다.

개발자는 위의 내용정도만 이해하고 [사용]만 잘하면 된다.

pbkdf2 : 솔트값과 해시함수의 반복횟수등을 지정할 수 있다. 해당 프로그래밍언어와 같이 검색해보면 됨.

bcrypt : 패스워드를 위해 탄생해서 아주 강력한 해시 알고리즘이 적용됨. 마찬가지로 검색해서 사용 (Spring security에서 사용함)

암호화(Encryption)는 복호화가 필요한 데이터 혹은 전에 언급했던 Https처럼 통신(송수신)에 있어서 사용 된다.



출처: https://jeong-pro.tistory.com/92?category=793347 [기본기를 쌓는 정아마추어 코딩블로그]

Spring MVC 처리 순서

1. 클라이언트(Client)가 서버에 어떤 요청(Request)을 한다면 스프링에서 제공하는 DispatcherServlet 이라는 클래스(일종의 front controller)가 요청을 가로챈다. (web.xml에 살펴보면 모든 url ( / )에 서블릿 매핑을하여 모든 요청을 DispatcherServlet이 가로채게 해둠(변경 가능))

2. 요청을 가로챈 DispatcherServlet은 HandlerMapping(URL 분석등..)에게 어떤 컨트롤러에게 요청을 위임하면 좋을지 물어본다. (HandlerMapping은 servlet-context.xml에서 @Controller로 등록한 것들을 스캔해서 찾아놨기 때문에 어느 컨트롤러에게 요청을 위임해야할지 알고 있다.) 요청을 처리할 컨트롤러 빈 객체를 DispatcherServlet에 다시 전달한다.

3. 요청에 매핑된 컨트롤러가 있다면 @RequestMapping을 통하여 요청을 처리할 메서드에 도달한다. 콘트롤러 빈 객체는 여러 종류가 존재한다(@Controller를 사용하는 컨트롤러, Controller 인터페이스 구현체, HttpRequestHandler 인터페이스 구현체 등). 이 모든 방법은 동일하게 처리 할 수 있도록 DispatcherServlet은 HandlerAdapter 빈에게 요청 처리를 위임한다.

4. 컨트롤러에서는 해당 요청을 처리할 Service를 주입(DI)받아 비즈니스로직을 Service에게 위임한다.

5. Service에서는 요청에 필요한 작업 대부분(코딩)을 담당하며 데이터베이스에 접근이 필요하면 DAO를 주입받아 DB처리는 DAO에게 위임한다.

6. DAO는 mybatis(또는 hibernate등) 설정을 이용해서 SQL 쿼리를 날려 DB에 저장되어있는 정보를 받아 서비스에게 다시 돌려준다. (이 때, 보통 Request와 함께 날아온 DTO 객체(@RequestParam, @RequestBody, ...)로 부터 DB 조회에 필요한 데이터를 받아와 쿼리를 만들어 보내고, 결과로 받은 Entity 객체를 가지고 Response에 필요한 DTO객체로 변환한다.)

7. 모든 비즈니스 로직을 끝낸 서비스가 결과물을 컨트롤러에게 넘긴다.

8. 결과물을 받은 컨트롤러는 필요에 따라 Model객체에 결과물 넣거나, 어떤 view(jsp)파일을 보여줄 것인지등의 정보를 담아 DispatcherServlet에게 보낸다.

9. DispatcherServlet은 ViewResolver에게 받은 뷰의 대한 정보를 넘긴다.

10. ViewResolver는 해당 JSP를 찾아서(응답할 View를 찾음) DispatcherServlet에게 알려준다.

(servlet-context.xml에서 suffix, prefix를 통해 /WEB-INF/views/index.jsp 이렇게 만들어주는 것도 ViewResolver)

11. DispatcherServlet은 응답할 View에게 Render를 지시하고 View는 응답 로직을 처리한다.

12. 결과적으로 DispatcherServlet이 클라이언트에게 렌더링된 View를 응답한다.



출처: https://jeong-pro.tistory.com/96 [기본기를 쌓는 정아마추어 코딩블로그]

프로젝트의

src/main/resuource에서

log4j.xml 파일의

<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
</root>

"warn" 부분을 "info"로 변경

 

<!-- Root Logger -->
<root>
<priority value="info" />
<appender-ref ref="console" />
</root>

 

이후 프로젝트 재실행

먼저 pom.xml에 Spring Security 관련한 라이브러리 추가

- spring-security-web
- spring-security-cofing 
- spring-secuirty-core 
- spring-security-taglibs

 

보통 Spring Security의 경우 단독으로 설정할 수 있기 때문에 기존의 root-context.xml이나 servlet-context.xml과는 별로도 security-context.xml을 작성하는 것이 좋음

 

이후 web.xml에 필터(springSecurityFilterChain)를 추가 및 추가 된 security-context.xml을 로딩하도록 설정

 

security-context.xml

기본형

<bean id="customAccessDenied" class="com.spring.security.CustomAccessDeniedHandler"></bean>

<security:http>
	<security:intercept-url pattern="/sample/all" access="permitAll"/>
	<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
	<security:access-denied-handler error-page="/accessError" />
	<security:access-denied-handler ref="customAccessDenied" />
	<security:form-login/>
</security:http>

작동은 intercept로 작동

- pattern 속성은 intercept가 작동할 페이지의 uri

- access 속성에서 조건을 작성

 o permitAll의 경우 인터셉트가 작동하지 않음

 o hasRole('')로 조건 설정 가능

 

인터셉트 작동시 작동 될 페이지 설정

- access-denied-handler

- error-page 속성을 이용해서 controller를 통해 커스텀 된 에러페이지 작성 후 이동 가능(문제점은 URI 자체의 변화가 없음)

- 접근 제한이 된 경우에 다양한 처리를 하고 싶다면 직접 AccessDeniedHandler 인터페이스를 구현하는 편이 좋음

ex) 쿠키나 세션에 특정한 작업을 하거나 HttpServletResponse에 특정한 헤더 정보를 추가하는 등의 행위를 할 경우

- 직접 구현 했을 경우 구현한 페이지를 빈으로 등록해서 사용(error-page와는 달리 redirect 처리 됨)

 

 

오라클 접속 시도 중에 에러메세지 뜸

 

원인 : 오라클 11?버전 이후 패스워드를 180일 내에 한번 바꿔줄 필요가 있음

안 바꿀 경우 계정이 막힘

 

1. 실행창 (원도우키 + r )

 

이후

sqlplus conn as sysdba

입력 후에 비밀번호 입력

접속 확인

 

alter user (사용자 이름) account unlock;

계정 변경 확인

alter user (사용자 이름) identified by (바꿀 비밀번호);

 

이후 다시 오라클에 들어가서 바뀐 비밀번호로 로그인 시도

 

-끝-

● MIME 타입은 다운로드 할 수 있는 'APPLICATION_OCTET_STREAM_VALUE'으로 지정

@GetMapping(value="/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)

 

 

● 'Content-Disposition'로 다운로드 시 저장되는 이름 설정

● 파일 이름에 대한 문자열 처리는 파일 이름이 한글인 경우 저장할 때 깨지는 문제를 막기 위함

try {
	headers.add("Content-Disposition", "attachment; filename=" + new String(resourceName.getBytes("UTF-8"), "ISO-8859-1"));
} catch (UnsupportedEncodingException e) {
	e.printStackTrace();
}

 

 

● Chrome과는 다르게 IE/Edge 브라우저에서의 파일 다운로드 시 한글 깨짐 문제

● HTTP 헤더 메세지에 'User-Agent' 값을 이용(디바이스의 정보를 알 수 있음)

- 브라우저의 종류, 모바일인지 데스크톱인지 혹은 브라우저 프로그램의 종류를 구분 가능

// 수정 전
public ResponseEntity<Resource> downloadFile(String fileName) {}

// 수정 후
public ResponseEntity<Resource> downloadFile(@RequestHeader("User-Agent")String userAgent, String fileName) {}

 

 

● IE, Edge, Chrome 3가지 경우에 따라 인코딩 방식을 다르게 해 다운로드 시에 한글이 깨지지 않게 처리

- useragnet 정보 참고 : ohgyun.com/292

// IE 브라우저의 경우(IE 브라우저의 엔진 이름 = "Trident")
if(userAgent.contains("Trident")) {
	downloadName = URLEncoder.encode(resourceOriginalName, "UTF-8").replaceAll("\\+", " ");
// Edge 브라우저의 경우
} else if(userAgent.contains("Edge")) {
	downloadName = URLEncoder.encode(resourceOriginalName, "UTF-8");
// Chrome 브라우저의 경우
} else {
    downloadName = new String(resourceName.getBytes("UTF-8"), "ISO-8859-1");
}

 

1. 발의 스탠스
스쿼트의 발은 11자와 v자 어떻게 두는 것이 좋을까?
- 11자가 허벅지 근육만 볼때는 더 효과적, 불편함=불안전성이 좀 더 높은 근육의 참여도
- 앉아서, 누워서 다리의 내회전 외회전 확인(전경=내회전)
(전경=고관절에 붙는 대퇴골이 앞으로 튀어나간 형태)앉아서, 누워서 바깥 = 11자가 자연스러움
(후경=대퇴골이 고관절에 붙는 각도가 엄청 평평하게 붙어있음)안쪽 = v자

2. 깊이 및 발목 유연성
- 벗윙크의 위치를 보고 다리의 간격 조절
- 위의 것이 마음에 들지 않을 경우 뒷꿈치에 각도를 높인 후 실시(대퇴 사두 앞쪽에 자극이 조금더 갈 순 있음)
- 발목 가동성 체크 및 폼롤러, 발목가동성 운동으로 확보

'운동' 카테고리의 다른 글

운동 프로그램 (2021.02.22~)  (0) 2021.02.23

 

'운동' 카테고리의 다른 글

스쿼트 발의 각도  (0) 2021.02.23

+ Recent posts