<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>J N Y 0</title>
    <link>https://jny0.tistory.com/</link>
    <description>성장일기</description>
    <language>ko</language>
    <pubDate>Thu, 27 Aug 2026 05:33:04 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>jny0</managingEditor>
    <image>
      <title>J N Y 0</title>
      <url>https://tistory1.daumcdn.net/tistory/6116836/attach/f96a065d34234a47806d0d879eb01df8</url>
      <link>https://jny0.tistory.com</link>
    </image>
    <item>
      <title>SWT 참고</title>
      <link>https://jny0.tistory.com/229</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/CatalogSWT-JFace-Eclipse.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/CatalogSWT-JFace-Eclipse.htm&lt;/a&gt;&lt;/p&gt;</description>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/229</guid>
      <comments>https://jny0.tistory.com/229#entry229comment</comments>
      <pubDate>Mon, 12 Feb 2024 23:35:49 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Redis 캐시 적용하기 (2/2)</title>
      <link>https://jny0.tistory.com/220</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Redis를 이용해 캐시를 적용해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스프링이 제공하는 캐시 추상화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링은 &lt;b&gt;AOP 방식&lt;/b&gt;으로 메소드에 캐시를 적용하는 기능을 제공하고 있다. 캐시는 @Transactional 어노테이션을 통해 트랜잭션을 제공하는 것과&amp;nbsp; 마찬가지로 AOP를 이용해 메서드 실행 과정에 투명하게 적용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 스프링은 특정 기술을 추상화 계층으로 가져가고 개발자에게는 인터페이스를 동일한 형태로 제공한다. 캐시도 마찬가지인데 &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;EhCacheManager, RedisCacheManager등의 다양한 기술들이 모두 CacheManager 인터페이스를 구현하고 있기 때문에 서버의 환경에 맞추어 다양한 캐시 기술들을 선택적으로 적용할 수 있다..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;이렇게 특정 기술에 종속되지 않게 하는 방식을 &lt;b&gt;PSA(Portable Service Abstraction)&lt;/b&gt;라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 상품 조회에 캐시를 적용하려고 하고 아래는 내가 캐시에 저장할 dto이다&lt;/p&gt;
&lt;pre id=&quot;code_1701365360718&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ProductResponse {
    private Long id;
    private String name;
    private String description;
    private Long price;
    private Long stock;
    private List&amp;lt;NutrientResponse&amp;gt; nutrients;
    private List&amp;lt;CategoryResponse&amp;gt; categories;
    private Long likeCount;
    private Long sellerId;
    private String sellerName;
    private boolean isLiked;
    private String imageFilePath;
    private LocalDateTime deleteDate;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 설정 및 빈 등록하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 세션 데이터 저장소로 이미 레디스를 사용하고 있는 상태였다.&lt;/p&gt;
&lt;pre id=&quot;code_1701365188751&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableRedisHttpSession
public class RedisConfig {

    @Value(&quot;${spring.data.redis.host}&quot;)
    private String redisHost;

    @Value(&quot;${spring.data.redis.port}&quot;)
    private int redisPort;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }

    @Bean
    public RedisTemplate&amp;lt;?, ?&amp;gt; redisTemplate() {
        RedisTemplate&amp;lt;?, ?&amp;gt; redisTemplate = new RedisTemplate&amp;lt;&amp;gt;();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());

        return redisTemplate;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RedisTemplate 빈을 등록해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701365235328&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableCaching
@Configuration
public class RedisCacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

           RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
                .defaultCacheConfig()
                .disableCachingNullValues()
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.
                                fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair
                                .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper))
                )
                .entryTtl(Duration.ofMinutes(5L)); // 캐쉬 저장 시간

        return RedisCacheManager
                .RedisCacheManagerBuilder
                .fromConnectionFactory(redisConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .build();
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시를 관리해 줄cacheManager를 빈에 등록하면서 캐시 정책을 설정한다.&lt;br /&gt;serializeKeysWith() 와 serializeValuesWith() 를 통해 Key와 Value에 대한 직렬화를 설정해주었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StringRedisSerializer : String값을 직렬화하여 저장&lt;/li&gt;
&lt;li&gt;GenericJackson2JsonRedisSerializer은 &lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;모든 classType을 json 형태로 저장할 수 있는 범용적인 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;Jackson2JsonRedisSerializer이고, &lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;classType을 함께 저장한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;2. 캐시 적용하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;@Cacheable 어노테이션을 통해서 간단하게 캐시를 설정할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701367254033&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional(readOnly = true)
@Cacheable(value = &quot;product&quot;, key = &quot;#productId&quot;)
public RsData&amp;lt;ProductResponse&amp;gt; get(Long productId) {
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot;&gt;&lt;b&gt;@Cacheable&lt;/b&gt;&lt;span&gt; 는 캐시를 저장/조회하기 위한 어노테이션으로 캐시에 데이터가 없을 경우에는 로직을 실행한 후에 캐시에 데이터를 추가하고, 캐시에 데이터가 있으면 데이터를 반환한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value 속성은 캐시의 이름을 지정하고, key 속성은 캐시의 키를 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시까지 적용했으니 앱을 실행하여 잘 동작하는 지 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;오류&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt; InvalidDefinitionException : &lt;b&gt;java.time.LocalDateTime not supported&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701367092407&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Java 8 date/time type `java.time.LocalDateTime` not supported by default: 
add Module &quot;com.fasterxml.jackson.datatype:jackson-datatype-jsr310&quot; to enable handling 
(through reference chain: com.mypill.global.rsdata.RsData[&quot;data&quot;]-&amp;gt;com.mypill.domain.product.dto.response.ProductResponse[&quot;deleteDate&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;오류를 만났다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GenericJackson2JsonRedisSerializer를 사용하면 클래스 타입에 상관없이 직렬화/역직렬화가 가능하지만 &lt;b&gt;날짜 타입에 대해서는 지원되지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 저장할 ProductResponse의 deleteDate 타입이 LocalDateTime 타입이라서 발생한 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ObjectMapper를 커스텀해서 문제를 해결해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701368179848&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class MapperConfig {

    public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd HH:mm:ss&quot;);

    @Bean
    public ObjectMapper serializingObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        javaTimeModule
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer())
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());

        objectMapper
                .registerModules(javaTimeModule, new Jdk8Module())
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        return objectMapper;
    }

    public class LocalDateTimeSerializer extends JsonSerializer&amp;lt;LocalDateTime&amp;gt; {

        @Override
        public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(value.format(FORMATTER));
        }
    }

    public class LocalDateTimeDeserializer extends JsonDeserializer&amp;lt;LocalDateTime&amp;gt; {

        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
            return LocalDateTime.parse(p.getValueAsString(), FORMATTER);
        }
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701368340820&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableCaching
@Configuration
public class RedisCacheConfig {

    @Autowired
    private ObjectMapper objectMapper;

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
                .defaultCacheConfig()
                .disableCachingNullValues()
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.
                                fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair
                                .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper))
                )
                .entryTtl(Duration.ofMinutes(5L)); // 캐쉬 저장 시간

        return RedisCacheManager
                .RedisCacheManagerBuilder
                .fromConnectionFactory(redisConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .build();
    }


}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Custom ObjectMapper를 생성하고 JavaTimeModule를 등록해주었다. 이걸 GenericJackson2JsonRedisSerializer의 파라미터로 넘겨주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 앱을 실행해서 확인해보자...!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;오류&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt; &lt;b&gt;ClassCastException&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701368429436&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;java.lang.ClassCastException: 
class java.util.LinkedHashMap cannot be cast to class com.mypill.global.rsdata.RsData 
(java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; 
com.mypill.global.rsdata.RsData is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @4799c353)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;으잉...? 또 다시 오류를 만났고 직렬화 할 때 반환 타입에 대한 정보가 없는 문제인 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GenericJackson2JsonRedisSerializer는 모든 클래스 타입의 정보를 저장한다고 알고있어서 좀 당황했다. 왜지??&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;검색해보니, 커스텀 ObjectMapper를 사용한 경우에는 예외적이라고 한다.&lt;b&gt; ObjectMapper가 직렬화/역직렬화시 클래스타입 정보를 포함하지 않기 때문에&lt;/b&gt; 해당 데이터에 타입 정보가 아예 존재하지 않는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해서는 ObjectMapper가 클래스 타입을 포함하도록 코드를 수정해주어야한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래 코드를 추가해주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1701368814194&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    objectMapper.activateDefaultTyping(
            BasicPolymorphicTypeValidator.builder()
                    .allowIfBaseType(Object.class).build(),
            ObjectMapper.DefaultTyping.NON_FINAL
    );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;activeDefaultTyping()&lt;/b&gt; 메서드를 통해서 타입을 설정할 수 있고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ObjectMapper.DefaultTyping을 NON_FINAL로 설정하면, 클래스의 최종 서브 클래스에 대해서만 타입 정보가 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f3c000;&quot;&gt;성공&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 앱을 실행해서 확인해보니 상품 페이지를 조회할 때 캐시가 잘 생성되어 저장되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;208&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxVUUr/btsBaXVkY1h/GL0XrEOhJdfx78teL396ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxVUUr/btsBaXVkY1h/GL0XrEOhJdfx78teL396ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxVUUr/btsBaXVkY1h/GL0XrEOhJdfx78teL396ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxVUUr%2FbtsBaXVkY1h%2FGL0XrEOhJdfx78teL396ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;208&quot; height=&quot;73&quot; data-origin-width=&quot;208&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 &lt;b&gt;Look Aside + Write Around&lt;/b&gt;&lt;span&gt; 전략을 사용할 것이기 때문에 &lt;b&gt;데이터가 변경되거나 삭제되었을 경우에는 캐시도 함께 삭제&lt;/b&gt;해주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;@CacheEvict&lt;/b&gt; 어노테이션을 사용하여 상품이 변경, 삭제될 때 캐시도 같이 삭제되도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701369230659&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
@CacheEvict(value = &quot;product&quot;, key = &quot;#productId&quot;)
public RsData&amp;lt;ProductResponse&amp;gt; update(Member actor, Long productId, ProductRequest request) {
    ...
}

@Transactional
@CacheEvict(value = &quot;product&quot;, key = &quot;#productId&quot;)
public RsData&amp;lt;ProductResponse&amp;gt; softDelete(Member actor, Long productId) {
    ...	
}

@Transactional
@CacheEvict(value = &quot;product&quot;, key = &quot;#productId&quot;)
public void updateStockAndSalesByOrder(Long productId, Long quantity) {
    ...	
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 상품이 삭제되었을 때 캐시도 같이 삭제되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVzz3V/btsBffUvgO7/VaZ54fk3vJgV5eMYkslZG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVzz3V/btsBffUvgO7/VaZ54fk3vJgV5eMYkslZG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVzz3V/btsBffUvgO7/VaZ54fk3vJgV5eMYkslZG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVzz3V%2FbtsBffUvgO7%2FVaZ54fk3vJgV5eMYkslZG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;216&quot; height=&quot;60&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;60&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://souljit2.tistory.com/72&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://souljit2.tistory.com/72&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@bagt/Redis-역직렬화-삽질기-feat.-RedisSerializer&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@bagt/Redis-역직렬화-삽질기-feat.-RedisSerializer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>트러블슈팅</category>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/220</guid>
      <comments>https://jny0.tistory.com/220#entry220comment</comments>
      <pubDate>Sun, 22 Oct 2023 01:09:55 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Redis 캐시 적용하기 (1/2)</title>
      <link>https://jny0.tistory.com/219</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트 개선 중 nGrinder로 부하테스트를 거치며 성능 개선을 위해 캐시를 적용하기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시는 데이터를 임시로 저장해준 뒤, 동일한 데이터 요청이 오면 DB에 접근하지 않고 저장된 캐시 데이터를 반환해주는 방식이다. &lt;b&gt;DB 접근 없이 요청을 처리&lt;/b&gt;할 수 있기 때문에 성능 개선 측면에서 많은 이점이 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 적용을 위해 고려해야할 사항&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 어떤 데이터에 캐시를 적용할 것인가&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시를 적용하는 것이 무조건 성능 개선을 가져오는 것이 아니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시는 생성, 수정, 삭제 연산보다는 조회 연산이 많은 데이터에 적합하다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자주 변경되는 데이터에 캐시를 적용하면 &lt;b&gt;데이터 정합성 문제&lt;/b&gt;가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;  데이터 정합성&lt;/b&gt; : 같은 데이터인데 캐시와 DB에 있는 정보가 서로 다른 경우&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - 변경이 많은 데이터일수록 데이터 일관성을 유지하기 위한 연산이 지속적으로 필요하기 때문에 &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;변경보다는 조회가 많은 데이터가 캐시를 적용하는 데 적절하다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 캐시 설계 전략&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;적절한 캐시 설계 전략을 선택해야 데이터 정합성 문제를 해결하면서도 성능 개선을 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 읽기 전략&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Look Aside&lt;/b&gt; : 데이터가 캐시에 있는 지 확인한 후 없으면 DB에서 조회&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Read Through&lt;/b&gt; : 데이터가 캐시에 있는지 확인 -&amp;gt; 없을 경우 DB에서 데이터를 조회하여 캐시 업데이트 -&amp;gt; 다시 캐시에서 데이터 조회&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; Look Aside와 비슷하지만 캐시 저장소에 캐시를 저장하는 주체가 DB이다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;직접적으로 DB에 접근하는 것을 막을 수 있지만 캐시에 장애가 발생하면 서비스가 전체 중단된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 쓰기 전략&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Write Back&lt;/b&gt;&amp;nbsp;: 모든 데이터를 캐시 저장소에 저장 -&amp;gt; 일정 시점마다 DB에 저장&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터를 캐시에 모아두었다가 일정 시점마다 DB에 반영하는 방법으로 쓰기 쿼리 횟수를 줄일 수 있다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 저장소에 장애가 발생하면 DB에 저장되기 전에 데이터가 유실될 수 있는 문제가 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Write Through&lt;/b&gt;&amp;nbsp;: 데이터 캐시 저장소에 저장 -&amp;gt; 바로 DB에 저장&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터를 캐시와&amp;nbsp; DB에 동시에 저장하는 방식으로 동기화가 유지되어 데이터 일관성을 유지할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;매 요청마다 2번의 쓰기 작업이 발생하므로 상대적으로 속도가 느린 성능 문제가 있다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시에 넣은 데이터를 사용하지 않는 경우에는 리소스가 낭비도니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Write Around&lt;/b&gt; : 모든 데이터는 DB에 저장&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시를 갱신하지 않고 모든 데이터가 DB에 저장되는 방식이다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Write Through 보다 훨씬 빠른 속도를 갖는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시와 DB의 데이터가 다른 데이터 정합성 문제가 발생할 수 있다. 따라서 DB의 데이터가 수정, 삭제될 때마다 캐시도 함께 수정하거나 삭제해야한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다음 세가지의 조합을 일반적으로 많이 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Look Aside + Write Around&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Read Through + Write Around&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Read Through + Write Through&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;나는 프로젝트에서 1번 &lt;b&gt;Look Aside + Write Around &lt;/b&gt;조합으로 캐싱을 적용하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. Local Cache vs Global Cache&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시를 적용할 때 캐시를 각 서버에 저장할지 외부 서버에 따로 캐시 저장소를 둘 지 결정해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Local Cache&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버마다 캐시를 따로 저장하는 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;네트워크 통신이나 데이터를 외부에서 가져오는 등의 오베헤드가 없기 때문에 속도가 빠르다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 &lt;b&gt;다중 서버 환경에서 데이터 정합성 문제&lt;/b&gt;가 발생하며, 동기화를 위한 비용이 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Global Cache&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부에 따로 캐시 저장소를 두는 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부 저장소에 접근하여 데이터를 가져오는 과정에서 네트워크 통신이 발생하기 때문에 로컬 캐시보다는 느리다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다중 서버 환경에서 모든 서버의 인스턴스가 동일한 캐시 저장소에 접근하기 때문에 &lt;b&gt;데이터의 일관성을 보장&lt;/b&gt;할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버의 인스턴스가 추가되어도 동일한 비용이 들기 때문에 서버가 고도화 될수록, 캐시 데이터 크기가 많을 수록 글로벌 캐시가 유리하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;데이터 정합성 문제가 서비스에 영향을 준다면 글로벌 캐싱 전략&lt;/b&gt;을 선택하는 것이 바람직하다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;나는 적용하려는 이커머스 프로젝트가 서버의 스케일 아웃 상황을 가정하고 있고, 특히 이커머스 플랫폼의 상품 같은 경우는 가격 정보가 동기화되지 않으면 서비스에 심각한 결함이 생기기 때문에 글로벌 캐싱 전략을 선택했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;추가적으로 글로벌 캐싱을 위한 외부 저장소를 선택해야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;난 &lt;b&gt;Redis&lt;/b&gt;를 쓰기로 결정했는데,&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Redis는 인메모리 DB로 속도가 빠르고&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; Redis 다양한 자료구조를 지원하기 때문에 다양한 캐싱 시나리오에 유리하며,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이미 프로젝트에 Redis가 적용되어있어 추가적인 설정이 필요 없었다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다음 포스트에서 계속..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/REDIS-&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/REDIS-&lt;/a&gt; -캐시Cache-설계-전략-지침-총정리&lt;/span&gt;&lt;/p&gt;</description>
      <category>트러블슈팅</category>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/219</guid>
      <comments>https://jny0.tistory.com/219#entry219comment</comments>
      <pubDate>Thu, 19 Oct 2023 16:28:11 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 경주로 건설 (BFS, DP) - Java</title>
      <link>https://jny0.tistory.com/218</link>
      <description>&lt;figure id=&quot;og_1697307012071&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/67259&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/RMkuj/hyT9Ln8Xdp/gQMoCbenqceMiFHitVRKdK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ck3wdJ/hyUdSzcGpf/4mxSVhIqToOgBWCG8kAFX1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/67259&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/67259&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/RMkuj/hyT9Ln8Xdp/gQMoCbenqceMiFHitVRKdK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ck3wdJ/hyUdSzcGpf/4mxSVhIqToOgBWCG8kAFX1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[카카오 인턴] 경주로 건설&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 설명&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceeZMY/btsytah8mbG/JQnsnkBoKcxv44AvmbOosk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceeZMY/btsytah8mbG/JQnsnkBoKcxv44AvmbOosk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceeZMY/btsytah8mbG/JQnsnkBoKcxv44AvmbOosk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceeZMY%2Fbtsytah8mbG%2FJQnsnkBoKcxv44AvmbOosk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;753&quot; height=&quot;230&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;건설회사의 설계사인&lt;span&gt;&amp;nbsp;&lt;/span&gt;죠르디는 고객사로부터 자동차 경주로 건설에 필요한 견적을 의뢰받았습니다.&lt;br /&gt;제공된 경주로 설계 도면에 따르면 경주로 부지는&lt;span&gt;&amp;nbsp;&lt;/span&gt;N x N&lt;span&gt;&amp;nbsp;&lt;/span&gt;크기의 정사각형 격자 형태이며 각 격자는&lt;span&gt;&amp;nbsp;&lt;/span&gt;1 x 1&lt;span&gt;&amp;nbsp;&lt;/span&gt;크기입니다.&lt;br /&gt;설계 도면에는 각 격자의 칸은&lt;span&gt;&amp;nbsp;&lt;/span&gt;0&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;1&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 채워져 있으며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;0은 칸이 비어 있음을&lt;span&gt;&amp;nbsp;&lt;/span&gt;1은 해당 칸이 벽으로 채워져 있음을 나타냅니다.&lt;br /&gt;경주로의 출발점은 (0, 0) 칸(좌측 상단)이며, 도착점은 (N-1, N-1) 칸(우측 하단)입니다. 죠르디는 출발점인 (0, 0) 칸에서 출발한 자동차가 도착점인 (N-1, N-1) 칸까지 무사히 도달할 수 있게 중간에 끊기지 않도록 경주로를 건설해야 합니다.&lt;br /&gt;경주로는 상, 하, 좌, 우로 인접한 두 빈 칸을 연결하여 건설할 수 있으며, 벽이 있는 칸에는 경주로를 건설할 수 없습니다.&lt;br /&gt;이때, 인접한 두 빈 칸을 상하 또는 좌우로 연결한 경주로를&lt;span&gt;&amp;nbsp;&lt;/span&gt;직선 도로&lt;span&gt;&amp;nbsp;&lt;/span&gt;라고 합니다.&lt;br /&gt;또한 두&lt;span&gt;&amp;nbsp;&lt;/span&gt;직선 도로가 서로 직각으로 만나는 지점을&lt;span&gt;&amp;nbsp;&lt;/span&gt;코너&lt;span&gt;&amp;nbsp;&lt;/span&gt;라고 부릅니다.&lt;br /&gt;건설 비용을 계산해 보니&lt;span&gt;&amp;nbsp;&lt;/span&gt;직선 도로&lt;span&gt;&amp;nbsp;&lt;/span&gt;하나를 만들 때는 100원이 소요되며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;코너를 하나 만들 때는 500원이 추가로 듭니다.&lt;br /&gt;죠르디는 견적서 작성을 위해 경주로를 건설하는 데 필요한 최소 비용을 계산해야 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어, 아래 그림은&lt;span&gt;&amp;nbsp;&lt;/span&gt;직선 도로&lt;span&gt;&amp;nbsp;&lt;/span&gt;6개와&lt;span&gt;&amp;nbsp;&lt;/span&gt;코너&lt;span&gt;&amp;nbsp;&lt;/span&gt;4개로 구성된 임의의 경주로 예시이며, 건설 비용은 6 x 100 + 4 x 500 = 2600원 입니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSrfD5/btsytXCEgmF/jG4YeZYZNiewXLU5B9N8Q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSrfD5/btsytXCEgmF/jG4YeZYZNiewXLU5B9N8Q1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSrfD5/btsytXCEgmF/jG4YeZYZNiewXLU5B9N8Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSrfD5%2FbtsytXCEgmF%2FjG4YeZYZNiewXLU5B9N8Q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;660&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;또 다른 예로, 아래 그림은&lt;span&gt;&amp;nbsp;&lt;/span&gt;직선 도로&lt;span&gt;&amp;nbsp;&lt;/span&gt;4개와&lt;span&gt;&amp;nbsp;&lt;/span&gt;코너&lt;span&gt;&amp;nbsp;&lt;/span&gt;1개로 구성된 경주로이며, 건설 비용은 4 x 100 + 1 x 500 = 900원 입니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL1R8U/btsyueqCvjG/k1WKxexoCh6p4KTkXT4z3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL1R8U/btsyueqCvjG/k1WKxexoCh6p4KTkXT4z3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL1R8U/btsyueqCvjG/k1WKxexoCh6p4KTkXT4z3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL1R8U%2FbtsyueqCvjG%2Fk1WKxexoCh6p4KTkXT4z3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;728&quot; height=&quot;728&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;도면의 상태(0은 비어 있음, 1은 벽)을 나타내는 2차원 배열 board가 매개변수로 주어질 때, 경주로를 건설하는데 필요한 최소 비용을 return 하도록 solution 함수를 완성해주세요.&lt;/p&gt;
&lt;b&gt;[제한사항]&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;board는 2차원 정사각 배열로 배열의 크기는 3 이상 25 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;board 배열의 각 원소의 값은 0 또는 1 입니다.
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;도면의 가장 왼쪽 상단 좌표는 (0, 0)이며, 가장 우측 하단 좌표는 (N-1, N-1) 입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;원소의 값 0은 칸이 비어 있어 도로 연결이 가능함을 1은 칸이 벽으로 채워져 있어 도로 연결이 불가능함을 나타냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;board는 항상 출발점에서 도착점까지 경주로를 건설할 수 있는 형태로 주어집니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;출발점과 도착점 칸의 원소의 값은 항상 0으로 주어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;처음 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전체코드&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Solution {
    static int[][] visited;
    static int[][] price;
    static int[] dx = {1, -1, 0, 0}; // 좌우상하
    static int[] dy = {0, 0, 1, -1};
    static int n;

    public int solution(int[][] board) {
        int answer = 0;

        n = board.length;
        price = new int[n][n];
        visited = new int[n][n];

        for (int i = 0; i &amp;lt; n; i++) {
            for (int j = 0; j &amp;lt; n; j++) {
                price[i][j] = Integer.MAX_VALUE;
            }
        }

        bfs(0, 0, board);

        answer = price[n - 1][n - 1];
        System.out.println(Arrays.deepToString(price));
        return answer;
    }

    public static void bfs(int x, int y, int[][] board) {
        Queue&amp;lt;Node&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
        queue.offer(new Node(x, y));
        visited[x][y] = 2;
        price[0][0] = 0;

        while (!queue.isEmpty()) {
            Node now = queue.poll();
            for (int i = 0; i &amp;lt; 4; i++) {
                int moveX = now.x + dx[i];
                int moveY = now.y + dy[i];

                int addPrice = 100;
                int nowPrice = price[now.x][now.y];

        // 코너 건설 여부 판단 -&amp;gt; 비용 추가
                if (!(now.x == x &amp;amp;&amp;amp; now.y == x)) { 
                    int backX = now.x - dx[i]; 
                    int backY = now.y - dy[i];
                    if (backX &amp;lt; 0 || backX &amp;gt;= n || backY &amp;lt; 0 || backY &amp;gt;= n || price[backX][backY] &amp;gt;= nowPrice) {
                        addPrice += 500;
                    }
                }

                if (moveX &amp;lt; 0 || moveX &amp;gt;= n || moveY &amp;lt; 0 || moveY &amp;gt;= n || board[moveX][moveY] == 1 || visited[moveX][moveY] == 2) {
                    continue;
                }

                queue.add(new Node(moveX, moveY));
                visited[moveX][moveY]++;
                price[moveX][moveY] = Math.min(price[moveX][moveY], nowPrice + addPrice);
            }
        }

    }


    public static class Node {
        int x;
        int y;

        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 bfs + dp 알고리즘으로 일단 풀이했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bfs로 순회하며 price 배열에 현재 노드까지 건설 가능한 도로의 최소 비용을 저장한다 (dp)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코너 건설 여부는 진행방향의 역방향 이전 노드의 가격과, 현재 노드의 가격을 비교해서 판단했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;PNG 이미지.png&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;771&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q2VHD/btsyuJ4WISK/nyPpK0M3kAxZeviJeEQeR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q2VHD/btsyuJ4WISK/nyPpK0M3kAxZeviJeEQeR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q2VHD/btsyuJ4WISK/nyPpK0M3kAxZeviJeEQeR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq2VHD%2FbtsyuJ4WISK%2FnyPpK0M3kAxZeviJeEQeR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;276&quot; height=&quot;281&quot; data-filename=&quot;PNG 이미지.png&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;771&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;int moveX = now.x + dx[i]; // 진행방향 다음 노드
int moveY = now.y + dy[i];

if (!(now.x == 0 &amp;amp;&amp;amp; now.y == 0)) { 
    int backX = now.x - dx[i]; // 진행방향의 역방향 이전 노드
    int backY = now.y - dy[i];
    if (backX &amp;lt; 0 || backX &amp;gt;= n || backY &amp;lt; 0 || backY &amp;gt;= n || price[backX][backY] &amp;gt;= nowPrice) {
        addPrice += 500;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;price 배열을 &lt;code&gt;Integer.MAX_VALUE&lt;/code&gt; 로 초기화 해주었기 때문에, 이전노드의 가격이 지금노드의 가격보다 크거나 같으면 해당 방향에서 온 것이 아니다. 그러므로 코너 건설 비용을 추가해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역방향 이전 노드가 범위를 넘어가는 경우에도 코너를 건설해야한다. 단, 맨 처음 노드인 0,0은 예외이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;visited 배열로 방문 여부를 확인해주었는데, 여기서 꼬인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;한 번 방문한 노드를 방문하지 않으면 &lt;/span&gt;입출력 예 4번처럼 경로가 2가지일 경우에 최소 가격을 찾지 못하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 노드 당 2번까지 방문이 가능하게 로직을 구성했는데, 경로가 한가지인 1,2,3번 테스트케이스는 통과했지만 4번은 통과가 되지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;채점 결과&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;정확성: 44.0&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;합계: 44.0 / 100.0&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;틀린 TC : 6~9, 11, 13, 15~20, 22, 25&lt;/span&gt;&lt;/blockquote&gt;
&lt;div style=&quot;color: #5f7f90;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 사람들의 풀이를 검색해보니 &lt;b&gt;visited를 방향까지 저장하는 3차원배열&lt;/b&gt;로 구현하는 풀이가 대부분이였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러번 방문이 가능하기 때문에 &lt;b&gt;4가지 방향에서 오는 경우를 모두 저장&lt;/b&gt;하기 위함인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 보고 내 풀이 코드에서 4번까지 방문 가능하도록 코드를 수정했더니 22번 코드 하나 더 통과되고 나머지는 여전히 실패였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;방문 횟수를 카운트하는 것은 예외사항이 너무 많기 때문일까?&lt;span&gt; -&amp;gt; 같은 방향에서 계속 오는 경우?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3차원 배열을 써서 다시 풀어 보자...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최종 풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp 배열을 따로 만들지 않고 board 배열을 활용해서 메모이제이션을 구성했다.&lt;/p&gt;
&lt;pre id=&quot;code_1697306919380&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    static int[][][] visited;
    static int[] dx = {1, -1, 0, 0}; // 좌우상하
    static int[] dy = {0, 0, 1, -1};
    static int n;

    public int solution(int[][] board) {
        n = board.length;
        visited = new int[n][n][4];

        return bfs(0, 0, -1, 0, board);
    }

    public static int bfs(int x, int y, int dir, int cost, int[][] board) {
        Queue&amp;lt;Node&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
        queue.offer(new Node(x, y, dir, cost));

        int minCost = Integer.MAX_VALUE;

        while (!queue.isEmpty()) {
            Node now = queue.poll();

            if(now.x == n-1 &amp;amp;&amp;amp; now.y == n-1){
                minCost = Math.min(minCost, now.cost);
            }

            for (int d = 0; d &amp;lt; 4; d++) { 
                int moveX = now.x + dx[d];
                int moveY = now.y + dy[d];

                if (moveX &amp;lt; 0 || moveX &amp;gt;= n || moveY &amp;lt; 0 || moveY &amp;gt;= n || board[moveX][moveY] == 1) {
                    continue;
                }
                int nextCost = now.cost;

                // 처음노드이거나 이전과 같은 방향인 경우에만 직선도로 건설 그 외에는 코너 건설
                if(now.dir == -1 || now.dir == d) nextCost += 100;
                else nextCost += 600;

                // 해당 방향으로 노드를 방문하지 않았거나 최소가격으로 업데이트가 가능한 경우
                if(visited[moveX][moveY][d] == 0 || board[moveX][moveY] &amp;gt;= nextCost){
                    queue.add(new Node(moveX, moveY, d, nextCost));
                    visited[moveX][moveY][d] = 1;
                    board[moveX][moveY] = nextCost;
                }
            }
        }
        return minCost;
    }

    public static class Node {
        int x, y, dir, cost;

        public Node(int x, int y, int dir, int cost) {
            this.x = x;
            this.y = y;
            this.dir = dir;
            this.cost = cost;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향까지 고려해야하는 경우 3차원 배열을 사용하자....!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;board의 크기가 작기 때문에 충분히 시간안에 통과 가능한 코드가 나온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;오늘도 한단계 성장...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/DP</category>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/218</guid>
      <comments>https://jny0.tistory.com/218#entry218comment</comments>
      <pubDate>Sun, 15 Oct 2023 03:12:38 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Redis를 통한 세션 불일치 문제 해결 (2/2)</title>
      <link>https://jny0.tistory.com/217</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 프로젝트에 Redis를 적용해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 의존성 추가 및 환경 설정&lt;/h3&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;    //레디스
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    implementation 'org.springframework.session:spring-session-core'
    implementation 'org.springframework.session:spring-session-data-redis'&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;spring:
  data:
    redis:
      host: 127.0.0.1
      port: 6379&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle에 의존성을 추가해주고 applicaiton.yml 파일에 redis 정보를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. RedisConfig.java&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
@EnableRedisHttpSession
public class RedisConfig {

    @Value(&quot;${spring.data.redis.host}&quot;)
    private String redisHost;

    @Value(&quot;${spring.data.redis.port}&quot;)
    private int redisPort;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@EnabledRedisHttpSession&lt;/code&gt;을 사용해 세션저장소로 Redis를 사용하겠다고 명시해준다.&lt;br /&gt;.yml 파일에서 &lt;code&gt;session:store-type=redis&lt;/code&gt; 라고 명시해주는 것과 동일한 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.yml 파일에서 설정한 host와 port 값들을 주입해주고 레디스 연결 팩토리(RedisConnectionFactory)를 생성하여 반환한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  &lt;b&gt;Lettuce vs Jedis&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Redis Client의 오픈 소스는 대표적으로&amp;nbsp;Lettuce, Jedis가 있는데, 현재 프로젝트에서는 Lettuce를 사용하고 있고 Spring에서도 Lettuce를 default로 지정하고 있다.&lt;br /&gt;&lt;br /&gt;스레드 안정성이 대표적인 이유이다.&amp;nbsp;&lt;br /&gt;Jedis의 경우에는 멀티 스레드 환경에서 스레드 안정성을 보장하지 않지만, Lettuce는 netty 기반으로 멀테 스레드 환경에서 공유될 수 있다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 터미널 &lt;code&gt;redis-cli&lt;/code&gt; 에서 &lt;code&gt;keys \*&lt;/code&gt; 명령어를 통해 레디스에 저장된 모든 키 값을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 실행 후 로그인하여 키 값을 확인해보면 세션이 레디스에 저장된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;41&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEjcDq/btsBbQayjag/NyTsHraqjbHaB0ypmsdyQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEjcDq/btsBbQayjag/NyTsHraqjbHaB0ypmsdyQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEjcDq/btsBbQayjag/NyTsHraqjbHaB0ypmsdyQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEjcDq%2FbtsBbQayjag%2FNyTsHraqjbHaB0ypmsdyQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;605&quot; height=&quot;41&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;41&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그아웃을 해도 레디스에 세션 정보는 그대로 남아있는데 세션의 생명주기가 남아있기 때무이다. 세션의 만료시간이 지나면 레디스에서 세션 정보가 삭제된다. 세션 설정을 통해 만료시간을 조정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>트러블슈팅</category>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/217</guid>
      <comments>https://jny0.tistory.com/217#entry217comment</comments>
      <pubDate>Sat, 14 Oct 2023 21:41:20 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] OSI 7계층, TCP/IP, HTTP</title>
      <link>https://jny0.tistory.com/216</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;OSI 7계층&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;OSI 7계층은 국제 표준화 기구에서 네트워크 통신이 이루어지는 과정을 7단계로 나눈 네트워크 표준 모델이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 송신할 때는 높은 계층에서 낮은 계층으로 / 수신부에서는 낮은 계층에서 높은 계층으로 데이터를 전달한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 캡슐화&lt;/b&gt; : 데이터를 송신할 때 각 계층에서 필요한 정보를 추가해 데이터를 가공하는데 이 때 제어 정보를 담은 헤더나 트레일러를 붙이는 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;헤더는 데이터 앞에 붙고 트레일러는 데이터 뒤에 붙는다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로토콜&lt;/b&gt; : 통신 규약. 데이터를 송수신하기 위해 정한 규칙&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3E7TU/btsynZPegom/35i68AXE4JMecKTuhVssN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3E7TU/btsynZPegom/35i68AXE4JMecKTuhVssN1/img.png&quot; data-alt=&quot;https://huimang2.github.io/etc/iso-standard-7498&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3E7TU/btsynZPegom/35i68AXE4JMecKTuhVssN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3E7TU%2FbtsynZPegom%2F35i68AXE4JMecKTuhVssN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;281&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://huimang2.github.io/etc/iso-standard-7498&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;7계층(응용 계층) : 사용자에게 통신을 위한 서비스 제공. 인터페이스 역할&lt;/li&gt;
&lt;li&gt;6계층(표현 계층) : 데이터의 형식(Format)을 정의하는 계층 (코드 간의 번역을 담당)&lt;/li&gt;
&lt;li&gt;5계층(세션 계층) : 컴퓨터끼리 통신을 하기 위해 세션을 만드는 계층&lt;/li&gt;
&lt;li&gt;4계층(전송 계층) : 최종 수신 프로세스로 데이터의 전송을 담당하는 계층 (단위 :Segment) (ex. TCP, UDP)&lt;/li&gt;
&lt;li&gt;3계층(네트워크 계층) : 패킷을 목적지까지 가장 빠른 길로 전송하기 위한 계층 (단위 :Packet) (ex. Router)&lt;/li&gt;
&lt;li&gt;2계층(데이터링크 계층) : 데이터의 물리적인 전송과 에러 검출, 흐름 제어를 담당하는 계층 (단위 :frame) (ex. 이더넷)&lt;/li&gt;
&lt;li&gt;1계층(물리 계층) : 데이터를 전기 신호로 바꾸어주는 계층 (단위 :bit) (장비: 케이블,리피터,허브)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;TCP/IP 4계층&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;TCP/IP는 인터넷에서 데이터를 주고받기 위한 네트워크 프로토콜이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP : 데이터를 나눈 단위인 패킷의 전달 여부와 전송 순서를 보장하는 통신 방식&lt;/li&gt;
&lt;li&gt;IP&amp;nbsp; : 패킷을 빠르고 보내기 위한 통신 방식&lt;/li&gt;
&lt;li&gt;TCP/IP 기반 프로토콜에는 대표적으로 HTTP가 있다.&lt;/li&gt;
&lt;li&gt;TCP/IP에 맞춰 네트워크 통신 표준인 OST 7계층을 단순화 한 것이 TCP/IP 4계층이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nsVRu/btsyqvmNoud/kGZnlHUMzu9Sn2DymLymU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nsVRu/btsyqvmNoud/kGZnlHUMzu9Sn2DymLymU1/img.png&quot; data-alt=&quot;https://shlee0882.tistory.com/110&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nsVRu/btsyqvmNoud/kGZnlHUMzu9Sn2DymLymU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnsVRu%2FbtsyqvmNoud%2FkGZnlHUMzu9Sn2DymLymU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;354&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://shlee0882.tistory.com/110&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;4계층(응용 계층): 사용자와 소프트웨어를 연결해 주는 계층으로, HTTP, HTTPS, DNS 등의 프로토콜이 작동한다.&lt;/li&gt;
&lt;li&gt;3계층(전송 계층): 데이터의 신뢰성을 보장하며, 포트 번호로 데이터를 적절한 응용 프로그램에 전달하는 역할을 한다. TCP, UDP 등의 프로토콜이 전송 계층에 속한다. 전송 계층의 데이터 단위는 세그먼트라고 한다.&lt;/li&gt;
&lt;li&gt;2계층(인터넷 계층): 데이터를 최종 목적지까지 도달할 수 있게 하는 계층으로, IP가 대표적인 프로토콜이다. 인터넷 계층에서는 전송 계층으로부터 받은 데이터에 헤더를 붙여 캡슐화하는데, 이를 패킷 또는 데이터그램이라고 한다.&amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;1계층(네트워크 인터페이스 계층): 네트워크 접근 계층이라고도 하며, 데이터를 전기 신호로 변환하고 MAC 주소를 사용해 기기에 데이터를 전달한다. 이더넷, Wi-Fi 등이 해당 계층에 속하는 대표적인 프로토콜이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;패킷 : 네트워크에서 주고받는 데이터를 작게 분할한 단위 (데이터의 송신 주소, 수신 주소 등의 정보가 포함)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MAC 주소 : 48비트로 구성된 하드웨어 고유의 주소로, 네트워크 인터페이스 계층에서 사용한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;HTTP&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;인터넷상에서 데이터를 전송하기 위한 프로토콜로 TCP/IP 4계층에서 응용 계층에 속한다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;비연결성, 무상태성의 특징이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;비연결성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트에서 요청을 보낸 후 서버로부터 응답을 받으면 연결을 끊는 것&lt;/li&gt;
&lt;li&gt;서버에서 으답을 받고 나서도 연결을 유지하려면 자원을 사용하게 되는데 비연결성은 연결을 끊음으로서 자원을 아낄 수 있다.&lt;/li&gt;
&lt;li&gt;반대로 서버가 클라이언트를 기억할 수 없기 때문에 동일한 클라이언트에서 연속적으로 요청이 오면 연결과 연결해제 과정을 반복하게 되어 자원을 낭비할 수 있다.&lt;/li&gt;
&lt;li&gt;단점을 보안하기 위해 일정 시간 동안 연결을 유지할 수 있도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;HTTP Keep Alive&lt;/b&gt;를 사용하여 마지막 응답 이후 일정 시간동안 연결을 유지한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  HTTP Keep Alive&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;HTTP 연결 시 일정 시간 동안 요청을 유지할 수 있도록 사용하는 HTTP 헤더의 일종이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클라이언트에서 HTTP 요청을 보낼 때 연결 헤더에 Keep Alive를 추가해서 보내면 서버에서 연결을 유지할 시간을&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Keep Alive 헤더에 추가해 응답한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;무상태&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버에서 클라이언트의 상태를 저장하지 않는 것&lt;/li&gt;
&lt;li&gt;클라이언트가 이전에 요청한 사항을 서버에 저장하지 않기 때문에
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 요청에 필요한 데이터를 모두 가지고 있거나 (&lt;b&gt;쿠키&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;서버가 클라이언트로부터 받은 요청 사항을 모두 저장해야한다 (&lt;b&gt;세션&lt;/b&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;무상태의 장점은 서버 확장성이 높다는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;HTTP 메시지 구조&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청 라인(request line) : 요청 URI, 요청 방법, HTTP 버전 등을 포함한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;상태 라인(status line) : 요청에 대한 HTTP 상태 코드와 HTTP 버전을 포함한다.&amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;헤더(header) : 키-값으로 구성된 다수의 헤더 항목으로 구성된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;빈 줄(blank line) : 헤더의 끝을 나타내는 빈 줄로, 헤더와 바디를 구분한다.&amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;바디(body) : 요청할 때 요청 방법 메서드가 POST인 경우에만 바디가 있고, 그 외 메서드일 때는 비어 있는 상태로 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;HTTP 메서드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;POST&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 데이터를 생성할 때 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GET&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 데이터를 조회할 때 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PUT&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 데이터를 갱신할 때 사용&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PATCH&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 데이터를 일부 갱신할 때 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DELETE&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 데이터를 제거할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;HTTP 상태 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 상태 코드는 클라이언트의 요청에 대한 서버의 상태를 알려 주는 코드로, 세 자리 숫자로 표현한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1xx: 클라이언트로부터 요청을 받아 처리 중&lt;/li&gt;
&lt;li&gt;2xx: 요청을 성공적으로 처리함&lt;/li&gt;
&lt;li&gt;3xx: 요청을 처리하기 위해 추가 처리 필요&lt;/li&gt;
&lt;li&gt;4xx: 클라이언트 오류&lt;/li&gt;
&lt;li&gt;5xx: 서버 오류&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev-coco.tistory.com/161&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dev-coco.tistory.com/161&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;기술 면접 대비 CS전공 핵심요약집 | 이수진&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000208504237&quot;&gt;https://product.kyobobook.co.kr/detail/S000208504237&lt;/a&gt;&lt;/p&gt;</description>
      <category>CS</category>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/216</guid>
      <comments>https://jny0.tistory.com/216#entry216comment</comments>
      <pubDate>Sat, 14 Oct 2023 21:31:51 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] 쿠키, 세션, REST</title>
      <link>https://jny0.tistory.com/215</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;쿠키와 세션&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿠키&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트의 상태 정보를 클라이언트에 PC에 저장했다가 필요시 정보를 참조하거나 재사용 가능&lt;/li&gt;
&lt;li&gt;저장 위치 : 클라이언트&lt;/li&gt;
&lt;li&gt;만료 시점 : 쿠키 저장시 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세션&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일정 시간동안 같은 사용자로부터 들어오는 일련의 요구를 하나의 상태로 보고 그 상태를 유지시키는 기술&amp;nbsp;&lt;/li&gt;
&lt;li&gt;저장 위치 : 웹 서버&lt;/li&gt;
&lt;li&gt;만료 시점 : 브라우저 종료시 삭제 (기간 지정 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;속도&lt;/b&gt; : 쿠키 &amp;gt; 세션 (쿠키는 파일을 읽고, 세션은 요청마다 서버에서 처리를 해야함)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;보안&lt;/b&gt; : 쿠키 &amp;lt; 세션 (쿠키는 탈취와 변조가 가능하시만, 세션은 ID값만 가지고 있으므로 상대적으로 안전)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;REST API&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;REST&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REST는 HTTP 통신을 활용하기 위해 고안된 아키텍쳐이다.&lt;/li&gt;
&lt;li&gt;클라이언트는 URI로 표현된 자원을 HTTP 메서드를 이용해 CRUD 연산을 할 수 있다.&lt;/li&gt;
&lt;li&gt;자원을 명시에 연산을 수행하고 상태를 전달하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일관된 인터페이스 : 자원을 나타내는 URI를 HTTP 메서드로 조작하는 일관된 인터페이스를 사용한다.&lt;/li&gt;
&lt;li&gt;따라서 HTTP를 따르는 모든 플랫폼에서 REST를 사용할 수 있다&lt;/li&gt;
&lt;li&gt;클라이언트-서버 구조 : 클라이언트와 서버 간의 요청-응답의 독립적인 구조를 갖는다&lt;/li&gt;
&lt;li&gt;무상태성 : 서버에서는 클라이언트의 요청을 저장하거나 관리하지 않는다&lt;/li&gt;
&lt;li&gt;캐싱 가능 : HTTP 표준을 사용하므로 클라이언트는 이전에 서버로부터 받은 응답을 저장 및 재사용하는 캐싱을 할 수 있다&lt;/li&gt;
&lt;li&gt;자체 표현 구조 : REST API는 자원, 행위, 표현으로 구성되어 REST API 메시지를 보고 어떤 요청을 하는지 알 수 있다&lt;/li&gt;
&lt;li&gt;계층형 구조 : REST 서버는 다중 계층으로 구성될 수 있어서 보안, 암호화와 같은 계층을 추가해 서버에 대한 기능을 유연하게 확장할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;URI(Uniform Resource Identifier) :&lt;/b&gt; 인터넷에 있는 자원을 나타내는 주소. URI는 인터넷에서 요구하는 기본 조건으로 인터넷 프로토콜에 항상 붙어 다닌다. &amp;nbsp;URI의 하위 개념으로 URL, URN이 있다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;URL(Uniform Resource Locator)&lt;/b&gt; : 인터넷에서 자원의 위치를 알 수 있는 규약이다. 웹 사이트 주소와 인터넷의 모든 자원을 나타낼 수 있다.&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;URN(Uniform Resource Name)&lt;/b&gt; : 자원의 위치 정보가 아닌 실제 자원을 특정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;REST API&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REST를 기반으로한 인터페이스이다.&lt;/li&gt;
&lt;li&gt;REST 규칙을 지키며 API를 제공하는 서비스를 RESTful하다고 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자원에 대한 행위는 HTTP 메서드로 나타내며, HTTP 메서드나 행위에 대한 표현이 URI에 들어가면 안 된다.&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;HTTP 메서드는 명시적이어야 한다. 즉, 요청하려는 목적에 맞게 HTTP 메서드를 사용해야 한다.&lt;/li&gt;
&lt;li&gt;POST 메서드로 Create뿐 아니라 Update 같은 연산을 하면 명시적이라고 할 수 없다.&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;URI 경로는 슬래시(/)로 계층 관계를 표현하며, URI 마지막에 슬래시가 들어가면 안 된다.&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;URI 경로에는 언더바(_)를 사용하면 안 되고, 소문자 사용을 지향한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;토큰&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버가 접속한 클라이언트에게 &lt;b&gt;유일한 토큰&lt;/b&gt;을 부여&lt;/li&gt;
&lt;li&gt;세션 인증은 서버가 세션정보를 가지고 있어야하고 이를 &lt;b&gt;조회하는 과정&lt;/b&gt;이 필요하기 때문에 많은 &lt;b&gt;오버헤드&lt;/b&gt; 발생&lt;/li&gt;
&lt;li&gt;토크는 세션과 달리 서버가 아닌 클라이언트에 저장되기 대문에 서버의 부담을 덜 수 있다.&lt;/li&gt;
&lt;li&gt;쿠키와 세션이 없는 앱에서 서버와 통신할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인증방식&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 아이디와 비밀번호로 로그인&lt;/li&gt;
&lt;li&gt;서버측에서 사용자(클라이언트)에게 &lt;b&gt;유일한 토큰&lt;/b&gt;을 발급한다&lt;/li&gt;
&lt;li&gt;클라이언트는 서버 측에서 전달받은 토큰을 쿠키나 스토리지에 저장해두고 서버에 요청을 할 때마아다 해당 토큰을 HTTP 요청 헤더에 포함시켜 전달한다&lt;/li&gt;
&lt;li&gt;서버는 전달받은 토큰을 검증하고 응답한다. 토큰에 요청한 사람의 정보가 담겨있어 서버는 DB를 조회하지 않고 누가 요청하는지 알 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토큰 자체의 데이터 길이가 길어, 인증 요청이 많아질수록 네트워크 부하가 심해진다&lt;/li&gt;
&lt;li&gt;Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없다.&lt;/li&gt;
&lt;li&gt;토큰을 탈취당하면 대처하기 어렵다 (사용 기한을 제한하는 식으로 극복)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;프록시&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프록시 서버는 클라이언트와 서버 사이에 존재하는 중개자이다.&lt;/li&gt;
&lt;li&gt;클라이언트로 동작하기도 하고 서버로 동작하기도 한다.&lt;/li&gt;
&lt;li&gt;데이터를 필터링 또는 변환하거나, 방화벽을 세우거나, 캐시 서버로 동작할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;프록시 서버는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;네트워크 상 어디에 위치하느냐&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;, 혹은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;어느 방향으로 데이터를 제공하느냐&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;에 따라 포워드 프록시와 리버스 프록시로 나뉜다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;포워드 프록시&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MC2UM/btsyufiE0Yu/ylXCUvqv83cZlCkfeSq820/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MC2UM/btsyufiE0Yu/ylXCUvqv83cZlCkfeSq820/img.png&quot; data-alt=&quot;https://url.kr/9p576o&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MC2UM/btsyufiE0Yu/ylXCUvqv83cZlCkfeSq820/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMC2UM%2FbtsyufiE0Yu%2FylXCUvqv83cZlCkfeSq820%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;783&quot; height=&quot;274&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://url.kr/9p576o&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;프록시 서버는 &lt;/span&gt;&lt;b&gt;클라이언트 바로 뒤에&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;놓여 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;같은 내부망에 존재하는 클라이언트의 요청을 받아 인터넷을 통해 외부 서버에서 데이터를 가져와 클라이언트에게 응답&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;이점 : 클라이언트 보안, 캐싱, 암호화&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;리버스 프록시&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UTabk/btsysrEo1TP/6GNnxCkXgq62Aeq0oxI5Fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UTabk/btsysrEo1TP/6GNnxCkXgq62Aeq0oxI5Fk/img.png&quot; data-alt=&quot;https://url.kr/9p576o&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UTabk/btsysrEo1TP/6GNnxCkXgq62Aeq0oxI5Fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUTabk%2FbtsysrEo1TP%2F6GNnxCkXgq62Aeq0oxI5Fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;764&quot; height=&quot;198&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;244&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://url.kr/9p576o&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;리버스 프록시는 &lt;/span&gt;&lt;b&gt;웹서버/WAS 앞에 &lt;/b&gt;놓여 있다.&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;클라이언트는 웹서비스에 접근할때 웹서버에 요청하는 것이 아닌, 프록시로 요청하게 되고, 프록시가 배후(reverse)의 서버로부터 데이터를 가져오는 방식&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;이점 : 로드 밸런싱, 서버 보안, 캐싱, 암호화&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;XXS&lt;/b&gt; : 웹 브라우저에 악성 스크립트를 삽입하여 사용자 정보를 탈취하는 공격방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;웹 브라우저에서 사용자 입력값과 출력값을 철저히 검증해서 XSS를 예방할 수 있음&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SOP(Same-Origin-Policy)&amp;nbsp;&lt;/b&gt;: 다른 출처의 리소스를 사용하는 것에 제한하는 보안 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CORS(Cross-Origin Resource Sharing)&amp;nbsp;&lt;/b&gt;: 현재 접속한 도메인 말고 다른 도메인에 접근할 수 있도록 처리 해주는 웹 브라우저 표준 기술&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #37352f;&quot; data-token-index=&quot;0&quot;&gt;&lt;b&gt;SQL Injection&lt;/b&gt; : &lt;span data-token-index=&quot;0&quot;&gt;악의적인 SQL 쿼리문을 삽입하여 데이터베이스를 비정상적으로 조작하는 공격 기법&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #37352f;&quot; data-token-index=&quot;0&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 주기적으로 모니터링하고 비상 알림을 걸어두어 확인 가능&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;figure id=&quot;og_1697284695799&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;  Reverse Proxy / Forward Proxy 정의 &amp;amp; 차이 정리&quot; data-og-description=&quot;프록시(Proxy) 란? 프록시 서버는 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해 주는 컴퓨터 시스템이나 응용 프로그램을 가리킨다. 프록시(Proxy)란 '대리' &quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/NETWORK-%F0%9F%93%A1-Reverse-Proxy-Forward-Proxy-%EC%A0%95%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EC%A0%95%EB%A6%AC#%ED%8F%AC%EC%9B%8C%EB%93%9C_%ED%94%84%EB%A1%9D%EC%8B%9C_forward_proxy&quot; data-og-url=&quot;https://inpa.tistory.com/entry/NETWORK-%F0%9F%93%A1-Reverse-Proxy-Forward-Proxy-%EC%A0%95%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EC%A0%95%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DJQjq/hyUdR73zMF/v1BIL1Dv1VNHJPZxpmeMlk/img.jpg?width=800&amp;amp;height=444&amp;amp;face=0_0_800_444,https://scrap.kakaocdn.net/dn/escE0E/hyT9Dp5Fk4/swIhHPXKKtufBDPUA4SvEk/img.jpg?width=800&amp;amp;height=444&amp;amp;face=0_0_800_444,https://scrap.kakaocdn.net/dn/ng4Dn/hyUdZ55gp7/u9kOJpfAOkrhih0jBZ6tCk/img.png?width=1323&amp;amp;height=794&amp;amp;face=0_0_1323_794&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/NETWORK-%F0%9F%93%A1-Reverse-Proxy-Forward-Proxy-%EC%A0%95%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EC%A0%95%EB%A6%AC#%ED%8F%AC%EC%9B%8C%EB%93%9C_%ED%94%84%EB%A1%9D%EC%8B%9C_forward_proxy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/NETWORK-%F0%9F%93%A1-Reverse-Proxy-Forward-Proxy-%EC%A0%95%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EC%A0%95%EB%A6%AC#%ED%8F%AC%EC%9B%8C%EB%93%9C_%ED%94%84%EB%A1%9D%EC%8B%9C_forward_proxy&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DJQjq/hyUdR73zMF/v1BIL1Dv1VNHJPZxpmeMlk/img.jpg?width=800&amp;amp;height=444&amp;amp;face=0_0_800_444,https://scrap.kakaocdn.net/dn/escE0E/hyT9Dp5Fk4/swIhHPXKKtufBDPUA4SvEk/img.jpg?width=800&amp;amp;height=444&amp;amp;face=0_0_800_444,https://scrap.kakaocdn.net/dn/ng4Dn/hyUdZ55gp7/u9kOJpfAOkrhih0jBZ6tCk/img.png?width=1323&amp;amp;height=794&amp;amp;face=0_0_1323_794');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;  Reverse Proxy / Forward Proxy 정의 &amp;amp; 차이 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프록시(Proxy) 란? 프록시 서버는 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해 주는 컴퓨터 시스템이나 응용 프로그램을 가리킨다. 프록시(Proxy)란 '대리'&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;기술 면접 대비 CS전공 핵심요약집 | 이수진&lt;/span&gt;&lt;span style=&quot;color: #353638; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000208504237&quot;&gt;https://product.kyobobook.co.kr/detail/S000208504237&lt;/a&gt;&lt;/p&gt;</description>
      <category>CS</category>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/215</guid>
      <comments>https://jny0.tistory.com/215#entry215comment</comments>
      <pubDate>Sat, 14 Oct 2023 21:01:55 +0900</pubDate>
    </item>
    <item>
      <title>[운영체제] 기본개념, 커널, 시스템</title>
      <link>https://jny0.tistory.com/214</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;운영체제&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하드웨어 위에 설치되어 하드웨어 계층과 다른 소프트웨어 계층을 연결하는 소프트웨어 계층&lt;/li&gt;
&lt;li&gt;컴퓨터 시스템의 자원을 관리하고, 사용자가 컴퓨터를 사용할 수 있는 환경을 제공하는 역할 수행&lt;/li&gt;
&lt;li&gt;악성 프로그램에 대한 사용자와 운영체제 자신을 보호&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;운영체제의 목적&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처리 능력(throughput) 향상: OS는 자원 관리를 통해 일정 시간 내에 시스템이 처리하는 일의 양을 향상시킨다.&amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;반환 시간(turnaround time) 단축: OS는 사용자가 시스템에 요청한 작업을 완료할 때까지 소요되는 시간을 단축시킨다.&amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;사용 가능도(availability) 향상: 사용 가능도는 시스템 자원을 얼마나 빨리 제공할 수 있는가를 의미한다. OS는 사용자가 컴퓨터를 사용해야 할 때 자원을 즉시 사용할 수 있게 한다.&amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;신뢰도(reliability) 향상 : 신뢰도는 시스템이 주어진 문제를 정확하게 푸는지를 의미한다. OS는 입력 값에 대한 정확한 결과 값을 줄 수 있도록 신뢰도를 향상해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커널&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영체제의 핵심 서비스를 담당하는 부분으로 컴퓨터 하드웨어와 프로세스의 보안, 자원관리, 하드웨어 추상화 같은 중요한 역할을 수행한다.&lt;/li&gt;
&lt;li&gt;특히 자원 관리를 위해 CPU 스케줄링, 메모리 관리, 입출력 관리, 파일 시스템 관리 등을 담당한다.&lt;/li&gt;
&lt;li&gt;운영체제가 제공하는 서비스 중 커널에 포함되지 않는 서비스도 있다. ex.사용자 인터페이스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;커널 모드&lt;/b&gt; : 하드웨어에 직접 접근해 메모리 CPU와 같은 자원을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자 모드&lt;/b&gt; : 커널 모드의 자원에 접근할 수 없게 제한을 둔다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시스템 콜&lt;/b&gt; : 사용자 모으데서 실행된 프로세스가 자원에 접근하려면 시스템 콜을 호출해서 커널에 요청해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;시스템&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다중 프로그래밍 시스템&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리의 사용자 영역에 프로그램이 여러개 탑재되어 있고, CPU가 이를 번갈하 수행한다. (메모리 공간 분할해서 적재)&lt;/li&gt;
&lt;li&gt;수행하는 프로세스는 스케줄링을 통해 결정한다.&lt;/li&gt;
&lt;li&gt;입출력이 일어날 때만 스케줄링이 일어나, 한 프로그램의 입출력 작업이 완료되더라도 해당 프로그램의 작업을 다시 시작할 수 없다는 단점이 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시분할 시스템&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다중 프로그래밍 방식의 단점을 보완하기 위해 타임 슬라이스를 활용한다.&lt;/li&gt;
&lt;li&gt;타임 슬라이스 : 타이머 인터럽트를 통해 구현 (일정 시간 단위 분할)&lt;/li&gt;
&lt;li&gt;입출력 발생과 타임슬리이스에서 무조건 스케줄링을 진행한다. 이에 따라 입출력 후 대기시간이 줄어들어 대화가 빈번한 프로그램도 자연스럽게 수행될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;대화형 시스템&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자의 요청에 대한 결과를 곧바로 얻을 수 있는 시스템&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다중 처리기 시스템 (멀티 프로세서 시스템)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멀티 프로세싱은 다수의 프로세서가 협력적으로 여러 작업들을 동시에 처리하는 것을 말한다.&lt;/li&gt;
&lt;li&gt;다수의 작업을 병렬로 처리할 수 있고, 어떤 프로세서가 고장날지라도 작업이 중단되지 않는 장점이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀링(Polling&lt;/b&gt;) : 운영체제가 하드웨어장치의 상태레지스터를 읽음으로써 명령의 수신 여부를 주기적으로 확인하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인터럽트&lt;/b&gt; : &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;CPU가 프로그램을 실행하는 중에 I/O 하드웨어장치나 다른 예외 상황의 발생으로 인해 특별한 처리가 필요할 때 CPU에 이를 알려 처리하도록 하는 것&lt;/span&gt; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DMA(Direct Memory Access) :&lt;/b&gt; 직접 메모리 접근&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하드웨어장치의 들이 메모리에 직접 접근하여 읽거나 쓸 수 있도록 하는 기능&lt;/li&gt;
&lt;li&gt;CPU의 개입 없이&amp;nbsp;I/O&amp;nbsp;장치와 기억장치 사이의 데이터를 전송하는 접근 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동기식 I/O&lt;/b&gt; : 입출력이 일어날 때 순서를 보장함 - 해당 입출력 작업이 완료되어야지만 다음 입출력 요청 수행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비동기식 I/O&lt;/b&gt; : 입출력이 일어날 때 순서를 보장하지 않음 - 해당 입출력 작업의 완료를 기다리지 않고 다음 요청이 들어오면 제어권을 넘겨주어 다른 작업을 수행할 수 있도록 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS</category>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/214</guid>
      <comments>https://jny0.tistory.com/214#entry214comment</comments>
      <pubDate>Sat, 14 Oct 2023 18:03:57 +0900</pubDate>
    </item>
    <item>
      <title>[운영체제] 메모리 관리, 가상메모리</title>
      <link>https://jny0.tistory.com/213</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;연속 메모리 할당&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;멀티 프로세스 환경에서 여러 프로세스를 메모리에 연속적으로 로드하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고정 분할 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고정 분할은 메모리 영역을 분할한 뒤 각 영역에 프로세스를 할당하는 방식이다.&lt;/li&gt;
&lt;li&gt;분할된 영역의 크기는 서로 다를 수 있으며, 분할된 크기는 고정된다,&lt;/li&gt;
&lt;li&gt;메모리에 올릴 수 있는 프로세스의 수와 각 프로세스의 크기가 제한된다는 단점이 있다.&lt;/li&gt;
&lt;li&gt;단편화 문제가 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;외부 단편화&lt;/b&gt; : 프로세스를 할당하기 어려울만큼 작은 메모리 공간들로 인해 메모리가 낭비되는 현상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내부 단편화&lt;/b&gt; : 분할된 크기보다 작은 프로세스가 할당되어 메모리 공간이 남는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가변 분할 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;할당할 프로세스의 크기에 따라 메모리 공간을 분할하는 방식&lt;/li&gt;
&lt;li&gt;메모리 할당 알고리즘을 이용해 가용 메모리 공간에서 프로세스가 로드될 수 있는 메모리 공간을 찾는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최초 적합(first-fit)&lt;/b&gt; :&amp;nbsp;메모리 공간에서 프로세스의 크기만큼 비어 있는 메모리 공간을 찾아 차례대로 프로세스를 로드하는 방식&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 최적 적합(best-fit)&lt;/b&gt; : 할당하려는 프로세스 크기 이상인 가용 메모리 공간 중에서 가장 작은 공간에 프로세스 할당
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가용 메모리 공간을 모두 탐색해야함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 최악 적합(worst-fit)&lt;/b&gt; : 할당하려는 프로세스 크기 이상인 가용 메모리 공간 중에서 가장 큰 공간에 프로세스 할당
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가용 메모리 공간을 모두 탐색해야함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;버디 시스템&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단편화를 해결하기 위한 방법&lt;/li&gt;
&lt;li&gt;버디 시스템에서 일정 크기의 메모리 공간을 요청 받으면, 그 공간과 비슷한 크기의 메모리 공간을 마련&lt;/li&gt;
&lt;li&gt;메모리 공간이 다 쓰이고 해제되면 인접한 두 개의 공간을 합치면서 큰 공간을 마련&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스와핑&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리에 올라온 프로세스으들을 임시로 보조기억장치 일부 영역으로 쫓아내고, 생긴 빈 공간에 다른 프로세스들을 적재하여 실행하는 방식&lt;/li&gt;
&lt;li&gt;실제 물리 메모리보다 더 많은 프로세스를 수용할 수 있도록 물리 메모리가 초과 할당될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;비연속 메모리 할당&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;프로세스의 메모리 영역을 나눠서 메모리 공간에 저장하는 방법&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;페이징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스의 논리 메모리 영역과 물리 메모리 영역을 각각 일정한 크기의 &lt;b&gt;페이지&lt;/b&gt;와 &lt;b&gt;프레임&lt;/b&gt;으로 나눈다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;페이지와 프레임 크기는 동일하다.&lt;/li&gt;
&lt;li&gt;페이지와 프레임에는 각각 번호를 할당해 프로세스의 페이지와 메모리의 프레임을 매핑한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;페이지 테이블 : PCB에 저장되며, 프로세스의 페이지 정보와 페이지에 매핑하는 프레임의 주소 값을 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;세그멘테이션&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스의 메모리 영역을 논리적 단위인 세그먼트로 분할해 메모리 할당&lt;/li&gt;
&lt;li&gt;세그멘테이션 테이블을 사용하여 논리 주소를 물리 주소로 매핑&lt;/li&gt;
&lt;li&gt;프로세스의 메모리 영역을 논리적 단위로 나눠 저장하므로 단위별로 데이터를 보호하기 쉽다.&lt;/li&gt;
&lt;li&gt;세그먼트의 크기가 균등하지 않으서 프로세스의 할당/해제를 반복하는 과정에서 외부 단편화 문제가 발생할 수 있다.&lt;/li&gt;
&lt;li&gt;메모리에 로드된 스택 세그먼트 영역에서 오버플로가 발생하면 다른 프로세스와 메모리 영역이 겹칠 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;가상 메모리&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;프로세스의 일부만 메모리에 로드하고, 나머지는 디스크에 둔 상태로 프로세스를 실행하는 방식&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;페이지 폴트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램을 실행하다가 물리 메모리에 필요한 페이지가 없을 경우&lt;/li&gt;
&lt;li&gt;페이지 폴트가 발생하면 디스크에서 필요한 페이지를 스왑인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;페이지 교체 알고리즘&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;FIFO&lt;/b&gt; :&amp;nbsp;메모리에 가장 먼저 올라온 페이지부터 내쫓는 방식&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LRU&lt;/b&gt; :&amp;nbsp;가장 오랫동안 사용되지 않은 페이지를 교체하는 알고리즘
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 많이 참조되었지만 가장 오래전에 참조되었던 페이지를 내쫓는 문제가 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LFU&lt;/b&gt; :&amp;nbsp;가장 적게 참조되었던 페이지를 교체하는 알고리즘
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;교체된 페이지가 미래에 많이 참조되는 경우가 있을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스레싱&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 일정 수 이상의 프로그램을 실행했을 때 오히려 CPU 이용률이 떨어지는 상황&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;페이지 부재 빈도 알고리즘&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스의 페이지 부재율을 주기적으로 조사하고 이 값에 근거해서 각 프로세스에 할당할 메모리 양을 동적으로 조절&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS</category>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/213</guid>
      <comments>https://jny0.tistory.com/213#entry213comment</comments>
      <pubDate>Sat, 14 Oct 2023 17:40:39 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Redis를 통한 세션 불일치 문제 해결 (1/2)</title>
      <link>https://jny0.tistory.com/212</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;세션 불일치 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scale-out을 통해 분산 서버 환경을 구축했을 때 발생할 수 있는 가장 큰 문제점은 역시&amp;nbsp;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;세션 불일치 문제&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi28uJ/btsBfaFOMv1/q7zY4Al459ICs7ag8Jkj11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi28uJ/btsBfaFOMv1/q7zY4Al459ICs7ag8Jkj11/img.png&quot; data-alt=&quot;https://hudi.blog/session-consistency-issue/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi28uJ/btsBfaFOMv1/q7zY4Al459ICs7ag8Jkj11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi28uJ%2FbtsBfaFOMv1%2Fq7zY4Al459ICs7ag8Jkj11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;820&quot; height=&quot;352&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://hudi.blog/session-consistency-issue/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산 서버 환경에서는 &lt;u&gt;로드밸런서&lt;/u&gt;를 통해 트래픽을 분산하여 운영하고, 여러대의 서버는 세션 저장소를 독립적으로 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 클라이언트가 로그인 요청을 하고 로드밸런서를 통해 요청이 A서버로 전달되면 클라이언트의 세션 정보는 A서버에 저장된다. 이후에 클라이언트의 작업 요청이 A서버가 아닌 다른 서버로 전달된다면 해당 서버에는 세션 정보가 존재하지 않기 때문에 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 세션 불일치 문제라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 불일치 문제를 해결하기 위해서 크게 &lt;u&gt;3가지 방법&lt;/u&gt;을 적용해볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;1. Sticky Session 방식&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 클라이언트의 요청과 응답을 처리할 서버를 고정시키는 방식이다.&lt;/li&gt;
&lt;li&gt;1번 클라이언트의 요청은 모두 A서버에, 2번 클라이언트의 요청은 모두 B 서버에서 처리하도록 고정하면 세션 불일치 문제를 해결할 수 있다.&lt;/li&gt;
&lt;li&gt;문제점
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;특정 서버에 트래픽이 집중&lt;/b&gt; :&amp;nbsp;특정 클라이언트의 요청이 많아진다면 하나의 서버에 트래픽이 집중될 수 있고 결국 본래의 목적인 로드밸런싱의 효과가 없어질 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;세션 정보의 유실 가능성 &lt;/b&gt;- 세션 정보를 가지고 있는 서버에 장애가 발생하면 세션 정보가 모두 유실되는 문제가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;2. &lt;span data-token-index=&quot;0&quot;&gt;Session Clustering 방식&lt;/span&gt; &lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 서버에서 세션이 생성될 때 다른 서버의 세션 저장소로 복제하는 방식이다.&lt;/li&gt;
&lt;li&gt;A서버에 세션이 저장될 때 B, C 서버에 복제하여 같은 세션 정보를 갖고있다면 트래픽이 몰릴 걱정 없이 세션 불일치 문제를 해결할 수 있다&lt;/li&gt;
&lt;li&gt;문제점
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;비효율적인 메모리 사용&lt;/b&gt; : 같은 정보를 모든 세션 저장소에 저장하는 것&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 요청 트래픽 증가&lt;/b&gt; : 데이터의 변경이 발생할 때마다 복제 작업을 위한 네트워크 요청 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;3.&lt;span&gt; &lt;span data-token-index=&quot;0&quot;&gt;Session Storage 방식&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부에 별도의 세션 저장소를 두고 서버들이 이를 공유하여 세션 불일치 문제를 해결하는 방식이다.&lt;/li&gt;
&lt;li&gt;일반적인 Disk 데이터베이스(MySQL, 오라클 등)은 데이터의 영속성을 위해 사용하지만, &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;세션 정보는 &lt;b&gt;&lt;u&gt;영속성을 보장할 필요가 없기 때문&lt;/u&gt;&lt;/b&gt;에 비교적 입출력 속도가 빠른 &lt;b&gt;In-memory 데이터베이스&lt;/b&gt;를 사용한다&lt;/li&gt;
&lt;li&gt;트래픽 부하, 비효율적인 메모리 사용, 네트워크 트래픽 증가 문제 없이 세션 불일치 문제를 해결할 수 있다.&lt;/li&gt;
&lt;li&gt;하지만 세션 저장소에 장애가 발생하면 모든 세션 정보를 잃어버리는 문제가 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;u&gt;마스터-슬레이브 복제&lt;/u&gt;를 활용하여 동일한 세션 저장소를 하나 더 구축하는 식으로 해당 문제를 보완할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;Redis 선택&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 세션 저장소 방식을 사용해서 세션 불일치 문제를 해결하기로 결정했으면 이제 세션 저장소로 활용할 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;데이터베이스&lt;/span&gt;&lt;/span&gt;를 선택해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명한대로 세션 저장소는 영속성을 보장할 필요가 없기 때문에 비교적 속도가 빠른 인메모리 데이터베이스를 선택하게 된다. 가장 대표적인 In-memory DB인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Redis&lt;/b&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Memcached&lt;/b&gt;를 비교해 보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;color: #666666; text-align: center; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Redis&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Memcached&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;저장소&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot; colspan=&quot;2&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;In Memory Storage&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;저장 방식&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot; colspan=&quot;2&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Key-Value&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;데이터 타입&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;String, Set, Sorted Set, Hash, List&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;String&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;데이터 저장&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Memory, Disk&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Only Memory&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;메모리 재사용&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;메모리 재사용 하지 않음(명시적으로만 데이터 삭제 가능)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;메모리 부족시 LRU 알고리즘을 이용하여 데이터 삭제 후 메모리 재사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;&lt;b&gt;스레드&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Single Thread&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Multi Thread&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;&lt;b&gt;캐싱 용량&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Key, Value 모두 512MB&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Key name 250 byte,&amp;nbsp; Value 1MB&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Memcached&lt;/b&gt; 는 멀티 스레드를 지원하고 Redis에 비해 적은 메모리를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Redis&lt;/b&gt;는 다양한 자료구조를 지원하고&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; Replication, 스냅샷, Pub/Sub 같은 여러 기능들을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 서비스의 설계에 따라 적절히 선택하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;일반적으로 데이터 읽기 성능은 Redis가 데이터 쓰기 성능은 Memcached가 더 좋다고 알고있다. 세션 데이터는 생성하는 것보다 읽는 작업이 더 많은 데이터이기 때문에 이런 관점에서는 Redis가 더 적절할 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;또한 다양한 자료구조를 지원하기 때문에 이후에 캐시 저장소로 활용하기에도 Redis가 적절하다고 생각했다.&amp;nbsp; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis는 스프링에서 API를 지원하기 때문에 의존성 빌드를 통해 간단하게 적용시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 레퍼런스도 Memcached 보다 훨씬 풍부하기 때문에 프로젝트 적용에 조금 더 용이할 것이라고 판단하여 Redis를 적용하기로 결정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 적용 과정은 다음 포스트에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/session-consistency-issue/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hudi.blog/session-consistency-issue/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://deveric.tistory.com/65&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://deveric.tistory.com/65&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@sileeee/Redis-vs-Memcached&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@sileeee/Redis-vs-Memcached&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>트러블슈팅</category>
      <author>jny0</author>
      <guid isPermaLink="true">https://jny0.tistory.com/212</guid>
      <comments>https://jny0.tistory.com/212#entry212comment</comments>
      <pubDate>Thu, 12 Oct 2023 21:03:01 +0900</pubDate>
    </item>
  </channel>
</rss>