주간 devtips 5 -> github repo로 이전 예정

junto·2024년 6월 30일

devtips

목록 보기
5/5
post-thumbnail

C++ +, +=, push_back() 문자열 연산 속도 차이

#include <iostream>
#include <string>
#include <chrono>

int main() {
    // + 연산을 사용하는 경우
    auto start = std::chrono::high_resolution_clock::now();
    std::string result;
    for (int i = 0; i < 1'000'000; ++i) {
        result = result + 'a';
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = end - start;
    std::cout << "+ 연산 시간: " << duration.count() << " 초" << std::endl;

    // += 연산을 사용하는 경우
    start = std::chrono::high_resolution_clock::now();
    std::string result1;
    for (int i = 0; i < 1'000'000; ++i) {
        result1 += 'a';
    }
    end = std::chrono::high_resolution_clock::now();
    duration = end - start;
    std::cout << "+= 연산 시간: " << duration.count() << " 초" << std::endl;

    // push_back을 사용하는 경우
    start = std::chrono::high_resolution_clock::now();
    std::string result2;
    for (int i = 0; i < 1'000'000; ++i) {
        result2.push_back('a');
    }
    end = std::chrono::high_resolution_clock::now();
    duration = end - start;
    std::cout << "push_back 연산 시간: " << duration.count() << " 초" << std::endl;

    return 0;
}

  • push_back 연산은 문자열 끝에 단일 문자를 추가하는 연산으로 가장 빠르다. += 연산은 기존 문자열에 문자열을 덧붙이는 작업을 한다. +연산은 두 문자열을 결합하여 새로운 문자열을 만들기 때문에 가장 느리다.

C++ Iterator

1. C++ Iterator 저장해서 O(1)에 접근하기

  • C++에서 제공하는 STL을 사용하면, 특정 iterator에 접근하기 위해선 O(N)이 걸린다. 하지만 아래와 같이 특정 범위의 Iterator 배열을 선언하고, O(1)에 접근할 수도 있다.
 for(int i = 0; i < n + 1; i++)
    l.push_back(i);
    
 auto it = l.begin();
 for(int i = 0; i < n + 1; i++){
    l_it[i] = it;
    it++;        
 }
  • 위의 코드를 아래와 같이 수정하면 제대로 동작할까? 그 이유는?
 auto it = l.begin();
 for(int i = 0; i < n + 1; i++){
    l.push_back(i);
    l_it[i] = it;
    it++;        
 }
  • 처음 iterator가 가리키는 값이 0이 아니라 end를 가리킨다! 실수하지 않도록 조심하자.

2. Iterator 추후에 접근한다면 수정하지 않기

  • 특정 iterator 원소를 O(1)에 접근하기 위해 Iterator 배열에 담아뒀다고 해보자. 이때 iterator에 접근해서 가져오고, 수정하는 코드이다.
if (init < next) {
	l.insert(l_it[next], init);
	auto tmp = l_it[next];
	tmp--;
	l_it[init] = tmp;
}
else {
	l.insert(l.end(), init);
	l_it[init] = --l.end();
}
  • auto tmp를 만들지 않고, 직접 iterator를 수정하게 되면 iterator 가져오고 수정할 때마다 인덱스에 따라 iterator를 공유할 수 있다. 이는 추후 연산에서 iterator가 무효가 될 때 다른 곳에서 해당 iterator에 접근하면 segmentation fault 오류가 발생할 여지를 만든다.

CSRF

  • 최근 CSRF 설정과 CSRF 토큰에 대해 잘못 안 사실이 있었다.

1. Spring Security CSRF 설정

http.cors(
  (cors) ->
	cors.configurationSource(
	  request -> {
		CorsConfiguration config = new CorsConfiguration();

		config.setAllowedHeaders(Collections.singletonList("*"));
		config.setAllowedOrigins(
		  List.of("http://localhost:5173", "https://spacestory.duckdns.org"));
		config.setAllowedMethods(Collections.singletonList("*"));

		config.setAllowCredentials(true);
		config.setExposedHeaders(Arrays.asList("Set-Cookie", "refreshToken"));
		config.setMaxAge(jwtUtil.REFRESH_TOKEN_EXPIRED);

		return config;
	  }));
  • Spring Security에서 교차 출처 허용(CORS)하는 설정이다. config.setMaxAge가 무엇을 뜻하는 걸까? 처음에는 노출할 헤더를 언제까지 노출할지 설정하는 옵션으로 잘못 알았다.
  • PUT, DELETE와 같은 요청들에 대해 해당 요청을 서버가 처리할 수 있는지 미리 사전 요청을 보낸다. 이를 preflight 요청이라고 한다. 위의 설정에서 setMaxAge는 preflight 결과를 브라우저가 캐시하는 시간을 설정하는 것이다. 즉, 해당 시간 동안 CORS 요청이 발생하면 브라우저는 preflight 요청을 생략할 수 있다.

2. CSRF 토큰 적용하기

  • CSRF 토큰을 언제 적용해야 할까? CSRF 토큰은 무엇인가? 언제 적용해야 하는지 고민하지 않고, 무작정 적용하려고 했었다.
  • CSRF는 악의적인 사용자가 로그인한 사용자의 세션을 이용해 요청을 위조하는 보안 공격이다. 서버에서는 아래와 같이 세션을 무상태로 유지하여 JWT 토큰을 발급하는 로직이다.
http.sessionManagement(
        (session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
  • 이 경우 CSRF 토큰을 발급하면 어떻게 될까? 서버가 세션을 유지하지 않으니 매번 요청마다 새로운 CSRF 토큰이 발급된다. 사용자의 처음 요청과 그다음 요청이 CSRF 토큰이 다르니 매번 INVALID CSRF TOKEN 오류가 발생한다.
profile
꾸준하게

0개의 댓글