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;
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 오류가 발생한다.