<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>CHeRry</title>
        <link>https://velog.io/</link>
        <description>🍷</description>
        <lastBuildDate>Mon, 09 Jun 2025 18:00:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. CHeRry. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/cherry_031" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Spring Boot WebSocket]]></title>
            <link>https://velog.io/@cherry_031/Spring-Boot-WebSocket</link>
            <guid>https://velog.io/@cherry_031/Spring-Boot-WebSocket</guid>
            <pubDate>Mon, 09 Jun 2025 18:00:46 GMT</pubDate>
            <description><![CDATA[<p>플레이어가 참여할 수 있는 퀴즈 방, 채팅과 이모지 기능 등을 구현하기 위해 웹소켓을 공부했다.
메세징 기능을 위해 메모리 기반의 STOMP 메세지 브로커를 사용했다. 이외에도 Kafka, RabbitMQ 등 메세징 시스템을 사용할 수도 있다.</p>
<h2 id="websocket">WebSocket</h2>
<p>웹소켓은 단방향이 아닌 양방향 통신을 가능하게 해주는 프로토콜 기술로, 클라이언트와 서버 간 실시간 통신이 필요할 때 사용한다.</p>
<p>웹소켓은 Handshake 과정을 거쳐 연결을 수립한다. 연결이 수립되고 나면 소켓 연결이 닫힐 때까지 실시간으로 데이터를 주고 받을 수 있다.</p>
<pre><code>
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint(&quot;/ws-connection&quot;)
                .setAllowedOriginPatterns(&quot;*&quot;)
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker(&quot;/subscribe&quot;);
        registry.setApplicationDestinationPrefixes(&quot;/publish&quot;);
    }
}</code></pre><p>WebSocketConfig.java
백엔드에 구현한 웹소켓 설정 파일이다.</p>
<pre><code>function connect() {
    var socket = new WebSocket(&#39;/ws-connection&#39;);
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function () {
        setConnected(true);
        stompClient.subscribe(&#39;/subscribe/rooms/5&#39;, function (greeting) {
            console.log(greeting.body);
        });
    });
}</code></pre><p>클라이언트에서는 위와 같은 방법으로 연결한다고 한다.</p>
<p>플레이어가 입퇴장할 때 프론트에서 소켓 연결을 관리해주면, 그 소켓 정보를 이용해 백에서 만든 메세지 컨트롤러로 소켓 간 실시간 통신을 할 수 있는 것 같다.
이제 메세지가 오면 같은 연결 소켓의 유저들에게 메세지를 전달해주는 ChatController를 구현할 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[java Async]]></title>
            <link>https://velog.io/@cherry_031/java-Async</link>
            <guid>https://velog.io/@cherry_031/java-Async</guid>
            <pubDate>Fri, 03 May 2024 18:43:48 GMT</pubDate>
            <description><![CDATA[<p><strong>Java 비동기 처리 방법</strong></p>
<ol>
<li>@Async annotation 으로 간단하게 처리 가능. 클래스에 @EnableAsnyc를 추가하고, 비동기로 제공하고자 하는 메소드에 @Async 어노테이션을 추가하는 방식으로 가능함. @Async의 기본 설정은 SimpleAsyncTaskExecutor 이기 때문에 thread 관리를 위해서는 별도로 구성해주어야 함</li>
<li>Async thread pool 설정을 위한 별도의 @Configuration 클래스를 만들고, 해당 클래스에 @EnableAsync 어노테이션 추가. (기존 클래스에서는 제거). Config 클래스 내부에 ThreadPoolTaskExecutor를 반환하는 @Bean 메소드를 만들고, thread pool 설정을 해준다. Async 메소드를 제공하는 클래스에서는 해당 Bean의 이름을 통해 가져다 쓸 수 있다. (ex) @Async(&quot;beanName&quot;)
 </li>
</ol>
<p>**  
Java 비동기 처리 장점**
@Async 사용을 통해 method 수정없이 처리 가능
 **
Java @Async 사용시 고려사항 및 주의사항**</p>
<p>AOP의 특성 및 제약을 주의해서 사용해야 함 (비동기 메소드와 같은 클래스 내의 다른 메소드에서 비동기 메소드를 호출한다면, AOP proxy를 우회하기 때문에 비동기로 동작하지 않음)</p>
<p>Spring AOP의 두가지 모드1. Proxy 모드 : Spring AOP의 기본 모드이며, 순수한 자바 코드로 AOP 구현한다. Aspect를 특정 method와 엮는 작업(Weaving)이 자바 코드가 실행되는 시점이 이뤄진다. RTW(Run Time Weaving)으로 처리된다고 표현하고, 특정 method가 호출되는 런타임 시기에 해당 method의 Aspect 처리 여부가 결정된다.2. AspectJ 모드 : LTW(Load Time Weaving) - 클래스를 로드하는 과정에서 weaving, CTW(Compile Time Weaving) - compile 시에 weaving (ex) @EnableAsync(mode=AdviceMode.ASPECTJ) 와 같은 어노테이션으로 AspectJ 모드를 지정해주어 사용 가능</p>
<p><a href="http://dveamer.github.io/java/SpringAsyncAspectJ.html">http://dveamer.github.io/java/SpringAsyncAspectJ.html</a> <a href="https://velog.io/@gwontaeyong/Spring-AOP%EC%97%90%EC%84%9C-Proxy%EB%9E%80">https://velog.io/@gwontaeyong/Spring-AOP%EC%97%90%EC%84%9C-Proxy%EB%9E%80</a> (Spring AOP 원리)</p>
<p>public method에만 사용 가능 (메소드가 public 이어야 프록시가 될 수 있음)
Thread가 분리되므로 HttpServletRequest, Session 접근 불가 </p>
<p> **
ThreadPool 설정 이유, 단점
**</p>
<p>사용 이유 : 쓰레드 풀을 미리 생성해 둠으로써 프로그램 동작시 쓰레드를 생성하는데 별도의 딜레이가 필요하지 않고, 쓰레드를 재사용하여 시스템 자원을 줄일 수 있다. 다수의 사용자 요청을 빠르게 처리할 수 있다.
단점 : 쓰레드 갯수를 지나치게 많이 설정하는 경우 메모리 낭비가 발생할 수 있다.
출처: <a href="https://haloworld.tistory.com/147">https://haloworld.tistory.com/147</a> [Halo World:티스토리]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[java syncronized]]></title>
            <link>https://velog.io/@cherry_031/java-syncronized</link>
            <guid>https://velog.io/@cherry_031/java-syncronized</guid>
            <pubDate>Sun, 28 Apr 2024 19:55:54 GMT</pubDate>
            <description><![CDATA[<p>기능 개발 중</p>
<ol>
<li>데이터를 리스트에 넣기</li>
<li>리스트의 길이를 계산하기
두 가지 기능을 하는 메소드를 구현했다.</li>
</ol>
<p>이 메소드가 실행될 때 2번 작업에서 리스트의 길이는 정확히 1번 작업을 마친 직후의 길이여야 했는데, 여러 클라이언트가 이 메소드를 호출하면서 1번 작업과 2번 작업의 사이에 다른 클라이언트가 호출한 메소드의 1번 작업이 실행되어 리스트의 길이를 정확히 세지 못 하는 일이 생길 수도 있을 것 같았다.</p>
<p>그래서 이 메소드에 syncronized를 사용하여 스레드 동기화 처리를 했다.
작성한 코드는 다음과 같다.</p>
<pre><code>    public synchronized SubmitStatus addAnswer(Long playerId, String playerAnswer) {
        playerAnswers.add(Answer.builder()
                .user_id(playerId)
                .user(playerAnswer)
                .build());
        return checkSubmitStatus();
    }</code></pre><h3 id="스레드-동기화">스레드 동기화</h3>
<p> 
스레드 동기화는 멀티스레드 환경에서 여러 스레드가 하나의 공유자원에 동시에 접근하지 못하도록 막는 것이다. 공유데이터가 사용되어 동기화가 필요한 부분을 임계영역(critical section)이라고 부르며, 자바에서는 이 임계영역에 synchronized 키워드를 사용하여 여러 스레드가 동시에 접근하는 것을 금지함으로써 동기화를 할 수 있다. 
 </p>
<h3 id="synchronized-사용-방법">synchronized 사용 방법</h3>
<p> 
synchronized 키워드는 동기화가 필요한 메소드나 코드 블럭 앞에 사용한다. synchronized로 지정된 임계영역은 한 스레드가 이 영역에 접근하여 사용할 때 lock을 걸어 다른 스레드가 접근할 수 없게 한다. 해당 스레드가 이 임계영역의 코드를 다 실행한 후 벗어나게 되면 unlock 상태가 되고, 이후 대기하고 있던 다른 스레드가 이 임계영역에 접근하여 다시 lock을 걸고 사용할 수 있게 된다. 
 lock은 해당 객체당 하나씩 존재하며, synchronized로 설정된 임계영역은 lock 권한을 얻은 하나의 객체만이 독점적으로 사용하게 된다. </p>
<p><strong>1. 메소드에 적용</strong>
내가 작성한 코드와 같은 방식이다.</p>
<pre><code>    public synchronized SubmitStatus addAnswer(Long playerId, String playerAnswer) {
        playerAnswers.add(Answer.builder()
                .user_id(playerId)
                .user(playerAnswer)
                .build());
        return checkSubmitStatus();
    }</code></pre><p><strong>2. 코드 블럭에 적용</strong>
필요한 부분에만 this를 사용하여 적용하면 된다.</p>
<pre><code>    public SubmitStatus addAnswer(Long playerId, String playerAnswer) {
        synchronized(this) {
            playerAnswers.add(Answer.builder()
                    .user_id(playerId)
                    .user(playerAnswer)
                    .build());
            SubmitStatus submitStatus = checkSubmitStatus();
        }
        return submitStatus;
    }</code></pre><p>(참고: <a href="https://kadosholy.tistory.com/123">https://kadosholy.tistory.com/123</a>)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Portainer]]></title>
            <link>https://velog.io/@cherry_031/Portainer</link>
            <guid>https://velog.io/@cherry_031/Portainer</guid>
            <pubDate>Sat, 20 Apr 2024 13:46:30 GMT</pubDate>
            <description><![CDATA[<p>도커 컨테이너를 관리하기 위해 Portainer를 사용하고 있었는데 내가 실수로 컨테이너를 중지시켜 Portainer에 접속할 수 없게 되었다. 처음 Portainer 환경을 구축한 사람은 다른 팀원이었는데 그 팀원이 예비군에 가있는 상태여서 내가 해결했어야 했다. Portainer 사용법부터 도커 컨테이너를 구동할 때 환경 변수를 입력하는 법 등을 구글링 하면서 해결하려고 노력했는데 완벽하게 해결되지 않아 카카오톡으로 그 팀원의 도움을 받아 겨우 해결했다. 그래도 찾아보면서 Let’s encrypt라는 SSL 인증서에 대해서도 알게 되었고 Portainer 관리법에 대해서도 알 수 있었다.</p>
<h3 id="portainer">Portainer</h3>
<p><img src="https://velog.velcdn.com/images/cherry_031/post/04ab3c03-5465-426d-998d-e1015714e5e4/image.png" alt=""> 포테이너는 이렇게 생겼습니다.
웹 UI를 통해 컨테이너를 편하게 관리할 수 있습니다!</p>
<pre><code>docker run -d -p 8000:8000 -p 9000:9000 --name portainer</code></pre><p>와 같이 포테이너 컨테이너를 띄우면 사용할 수 있습니다.</p>
<h3 id="docker-run">Docker run</h3>
<p>docker container를 구동시킬 때 docker run 뒤에 여러 옵션을 붙일 수 있습니다.
<strong>--detach (-d)</strong>
컨테이너를 백그라운드에서 실행하도록 합니다. --detach 옵션을 사용하면 Docker는 컨테이너의 ID만 출력하고 터미널 입력을 바로 돌려줍니다. 이를 통해 사용자는 컨테이너가 실행되는 동안 다른 작업을 계속할 수 있습니다.
<strong>--name</strong>
컨테이너에 고유한 이름을 지정할 수 있습니다. 이 이름은 Docker 내에서 컨테이너를 식별하고 관리하는 데 사용됩니다. 이름을 지정하지 않으면 Docker는 자동으로 무작위의 이름을 생성합니다.
<strong>--volume (-v)</strong>
호스트 시스템의 디렉토리나 파일을 컨테이너 내부에 마운트하여 데이터를 컨테이너와 공유하거나 영구적으로 저장할 수 있게 합니다. 이 옵션은 데이터의 지속성을 보장하며, 여러 컨테이너 간에 파일을 공유하는 데도 유용합니다.
<strong>--env (-e)</strong>
환경 변수를 설정할 때 사용하는 옵션입니다. --env를 사용하면 컨테이너 내부에서 사용할 환경 변수를 정의할 수 있습니다. 이는 애플리케이션 설정이나 구성 정보를 컨테이너화된 애플리케이션에 전달하는 데 필수적입니다.
<strong>--publish (-p)</strong>
컨테이너의 포트를 호스트의 포트에 매핑합니다. 이를 통해 외부 네트워크에서 컨테이너의 애플리케이션에 접근할 수 있게 됩니다. --publish 옵션은 특정 포트 또는 포트 범위를 호스트와 링크하여 외부에서 내부 서비스를 사용할 수 있도록 합니다.</p>
<h3 id="lets-encrypt">Let&#39;s encrypt</h3>
<p>Let&#39;s Encrypt는 웹사이트의 보안을 강화하는 데 사용되는 무료 자동화 오픈 소스 인증 기관(Certificate Authority, CA)입니다. 이 서비스는 인터넷 보안을 강화하기 위해 개발되었으며, 웹사이트 운영자가 쉽게 SSL/TLS 인증서를 획득하여 HTTPS를 통한 안전한 웹 브라우징을 제공할 수 있도록 지원합니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring Boot] DynamoDB에서 데이터 읽어오기]]></title>
            <link>https://velog.io/@cherry_031/Spring-Boot-DynamoDB%EC%97%90%EC%84%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9D%BD%EC%96%B4%EC%98%A4%EA%B8%B0</link>
            <guid>https://velog.io/@cherry_031/Spring-Boot-DynamoDB%EC%97%90%EC%84%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9D%BD%EC%96%B4%EC%98%A4%EA%B8%B0</guid>
            <pubDate>Fri, 29 Mar 2024 18:12:54 GMT</pubDate>
            <description><![CDATA[<h2 id="aws-시크릿-매니저">AWS 시크릿 매니저</h2>
<p>파트 별로 따로 관리하고 있던 키 값을 한 번에 관리하기 위해 AWS Secret Manager를 사용했다.
먼저 AWS 시크릿매니저에 보안 암호 등록을 하고 AWS CLI 설치와 권한 설정을 했다.</p>
<h3 id="aws-시크릿-매니저-보안-암호-등록">AWS 시크릿 매니저 보안 암호 등록</h3>
<p><img src="https://velog.velcdn.com/images/cherry_031/post/97c7b0fd-7df5-457f-9183-71da2843a2a3/image.png" alt="">
보안 암호를 등록하면 이렇게 샘플 코드를 준다.</p>
<h3 id="aws-cli-설치와-권한-설정">AWS CLI 설치와 권한 설정</h3>
<p>설치</p>
<pre><code class="language-bash">$ curl &quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot; -o &quot;awscliv2.zip&quot;
$ unzip awscliv2.zip
$ sudo ./aws/install</code></pre>
<p>권한 설정</p>
<pre><code class="language-bash">$ aws configure</code></pre>
<pre><code class="language-bash">AWS Access Key ID [None]: 
AWS Secret Access Key [None]: 
Default region name [None]: 
Default output format [None]:</code></pre>
<p>확인</p>
<pre><code class="language-bash">$ aws sts get-caller-identity</code></pre>
<h2 id="spring-boot에서-dynamodb-데이터-읽기">Spring Boot에서 DynamoDB 데이터 읽기</h2>
<h3 id="buildgradle">build.gradle</h3>
<pre><code>
    implementation platform(&#39;software.amazon.awssdk:bom:2.21.44&#39;)
    implementation &#39;software.amazon.awssdk:secretsmanager&#39;
    implementation &#39;software.amazon.awssdk:regions&#39;
    implementation platform(&#39;com.amazonaws:aws-java-sdk-bom:1.11.1000&#39;)
    implementation &#39;com.amazonaws:aws-java-sdk-dynamodb&#39;</code></pre><p>필요한 라이브러리를 추가해준다.</p>
<p><img src="https://velog.velcdn.com/images/cherry_031/post/9940e683-6e23-4ce9-9aec-f760913a6b93/image.png" alt="">
DynamoDB에 저장되어있는 이 데이터를 읽어 올 것이다.</p>
<p>먼저 AWSConfig 파일을 작성해준다.</p>
<pre><code class="language-java">@Getter
@Configuration
public class AWSConfig {

    private final String accessKey;
    private final String secretKey;
    private final String region;

    public AWSConfig() {
        String key = getSecret();
        this.accessKey = key.substring(19, 39);
        this.secretKey = key.substring(66, 106);
        this.region = &quot;ap-northeast-2&quot;;
    }

    public static String getSecret() {

        String secretName = &quot;MySecretName&quot;;
        Region region = Region.of(&quot;ap-northeast-2&quot;);

        // Create a Secrets Manager client
        SecretsManagerClient client = SecretsManagerClient.builder()
                .region(region)
                .build();

        GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder()
                .secretId(secretName)
                .build();

        GetSecretValueResponse getSecretValueResponse;

        try {
            getSecretValueResponse = client.getSecretValue(getSecretValueRequest);
        } catch (Exception e) {
            // For a list of exceptions thrown, see
            // https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
            throw e;
        }

        return getSecretValueResponse.secretString();
    }
}</code></pre>
<p>AWS 시크릿 매니저에서 access key를 가져오는 코드다. 위에서 말한 보안 암호를 등록하면 주는 샘플 코드를 사용했다.
리턴 값이 &quot;AWS_ACCESS_KEY:키 값, AWS_SECRET_ACCESS_KEY:시크릿 키 값&quot;의 형태인 String으로 반환돼서 파싱을 해야한다. 찾아보니 해쉬 맵으로 하는 방법이 있는 거 같은데?? 자바 문법을 잘 몰라서;;; 일단은 substring으로 처리했다. 최악의 코드.</p>
<p>+) 수정한 코드 추가 (4/10)</p>
<pre><code>    public AWSConfig() {
        Map&lt;String, String&gt; awsKey = stringToMap(getSecret());
        this.accessKey = awsKey.get(&quot;AWS_ACCESS_KEY&quot;);
        this.secretKey = awsKey.get(&quot;AWS_SECRET_ACCESS_KEY&quot;);
        this.region = &quot;ap-northeast-2&quot;;
    }

    private Map&lt;String, String&gt; stringToMap(String value) {
        ObjectMapper objectMapper = new ObjectMapper();
        TypeReference&lt;Map&lt;String, String&gt;&gt; typeReference = new TypeReference&lt;Map&lt;String, String&gt;&gt;() {
        };

        Map&lt;String, String&gt; awsKey = new HashMap&lt;&gt;();
        try {
            awsKey = objectMapper.readValue(value, typeReference);
        } catch (JsonMappingException e) {
            log.error(&quot;JSON 구조와 매핑 문제 발생: {}&quot;, e.getMessage());
        } catch (JsonProcessingException e) {
            log.error(&quot;JSON 처리 중 예외 발생: {}&quot;, e.getMessage());
        }
        return awsKey;
    }</code></pre><p>아니면 <a href="https://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/patterns/manage-credentials-using-aws-secrets-manager.html">https://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/patterns/manage-credentials-using-aws-secrets-manager.html</a> 를 참고해서</p>
<pre><code>     String  host  =  secretsJson.get(&quot;host&quot;).textValue();
     String  port  =  secretsJson.get(&quot;port&quot;).textValue();
     String  dbname  =  secretsJson.get(&quot;dbname&quot;).textValue();
     String  username  =  secretsJson.get(&quot;username&quot;).textValue();
     String  password  =  secretsJson.get(&quot;password&quot;).textValue();</code></pre><p>이런 식으로 해도 됨</p>
<p>다음으로 DynamoDBConfig 파일을 작성한다.</p>
<pre><code class="language-java">
@Configuration
public class DynamoDBConfig {

    @Autowired
    private AWSConfig awsConfig;

    @Bean
    public DynamoDBMapper dynamoDBMapper() {
        return new DynamoDBMapper(amazonDynamoDBClient());
    }

    private AmazonDynamoDBClient amazonDynamoDBClient() {
        BasicAWSCredentials awsCredentials = new BasicAWSCredentials(awsConfig.getAccessKey(), awsConfig.getSecretKey());
        return (AmazonDynamoDBClient) AmazonDynamoDBAsyncClientBuilder.standard()
                .withRegion(awsConfig.getRegion())
                .withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
                .build();
    }
}</code></pre>
<p>DynamoDBMapper로 DynamoDB에 CRUD를 할 수 있는데, Bean으로 등록해서 하나의 객체만 생성되게 해준다.
(참고: <a href="https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/DynamoDBMapper.CRUDExample1.html">https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/DynamoDBMapper.CRUDExample1.html</a>)</p>
<p>DynamoDB에서 데이터를 읽어올 테이블을 지정하고 데이터의 값을 매핑할 클래스를 만든다.</p>
<pre><code class="language-java">@Getter
@Setter
@Component
@NoArgsConstructor
@AllArgsConstructor
@DynamoDBTable(tableName = &quot;MyTable&quot;)
public class DynamoDBResponse {

    @DynamoDBHashKey(attributeName = &quot;month&quot;)
    private Integer month;

    @DynamoDBRangeKey(attributeName = &quot;timestamp&quot;)
    private String timestamp;

    @DynamoDBAttribute(attributeName = &quot;id&quot;)
    private String id;

    @DynamoDBAttribute(attributeName = &quot;description&quot;)
    private String description;
}</code></pre>
<p>HashKey(Partition Key)만 있으면 HashKey가 기본키 역할을 하고, HashKey랑 RangeKey가 같이 있으면 둘이 복합키로 기본키 역할을 한다.
(참고: <a href="https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey">https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey</a>)</p>
<p>이제 드디어 데이터를 읽어오는 DynamoDBService 파일을 작성한다.</p>
<pre><code class="language-java">@Service
public class DynamoDBService {

    @Autowired
    private DynamoDBMapper mapper;

    public DynamoDBResponse getData() {
        DynamoDBResponse response = mapper.load(DynamoDBResponse.class, 0, &quot;1&quot;);
        if (response == null) {
            throw new RuntimeException(&quot;Data not found for id: &quot;);
        }
        return response;

    }
}</code></pre>
<p>mapper.load()에 들어가는 파라미터는 위에서 만든 데이터 클래스, 파티션 키 값, 정렬 키 값이다. 파라미터로 넣은 키 값으로 DynamoDB에서 데이터를 찾는다. 물론 파티션 키만 있는 테이블이면 파라미터로 파티션 키만 넣어주면 된다.
(참고: <a href="https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/DynamoDBMapper.Methods.html#DynamoDBMapper.Methods.load">https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/DynamoDBMapper.Methods.html#DynamoDBMapper.Methods.load</a>)</p>
<p>Service 파일에서</p>
<pre><code>String description = dynamoDBService.getData().getDescription();</code></pre><p>이런 식으로 쓰면 된다.</p>
<h3 id="포스트맨으로-테스트-해보니-성공이다">포스트맨으로 테스트 해보니 성공이다!</h3>
<p>되는 건 확인했는데... 고쳐야 할 것들이 많다~</p>
<ol>
<li>key 파싱 방법 바꾸기</li>
<li>yml 파일에 key를 저장할 순 없을까?</li>
<li>DynamoDB 데이터의 값 중 List 타입을 가져오려고 클래스에 List로 정의했는데 TypeError로 데이터를 가져오지 못 했음</li>
</ol>
<p>그래도 DynamoDB에서 데이터 읽는 건 처음이었는데 구현해보고 작동하는 걸 확인해서 좋았다!!</p>
<p>참고자료:
(<a href="https://www.theprogrammerguide.com/post/aws-dynamodb_spring/">https://www.theprogrammerguide.com/post/aws-dynamodb_spring/</a>)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot Websocket 서버 실행과 테스트]]></title>
            <link>https://velog.io/@cherry_031/Spring-Boot-Websocket-%EC%84%9C%EB%B2%84-%EC%8B%A4%ED%96%89%EA%B3%BC-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@cherry_031/Spring-Boot-Websocket-%EC%84%9C%EB%B2%84-%EC%8B%A4%ED%96%89%EA%B3%BC-%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Fri, 22 Mar 2024 00:45:26 GMT</pubDate>
            <description><![CDATA[<p>저번 주에 이어서 이번 주도 웹소켓 공부를 했다. 웹 기초 지식이 없어서 서비스 흐름을 이해하는 데 너무 오래 걸렸다. 소켓 연결은 클라이언트에서 수행하고 서버에서는 전송된 메세지를 어디에 배포할지를 정의하는 메소드를 구현하면 된다고 이해했다.</p>
<p><strong>WebSocketConfig</strong> 웹소켓 관련 설정 파일</p>
<pre><code class="language-java">@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint(&quot;/ws&quot;)
                .setAllowedOriginPatterns(&quot;*&quot;)
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker(&quot;/sub&quot;);
        registry.setApplicationDestinationPrefixes(&quot;/pub&quot;);
    }
}
</code></pre>
<p>Endpoint(/ws)으로 클라이언트가 웹 소켓 연결 요청을 하면 handshake 과정을 거쳐 연결이 됨!</p>
<p><strong>withSocketJS()</strong></p>
<ul>
<li>SocketJS는 웹소켓을 지원하지 않는 브라우저에서 HTTP의 polling과 같은 방식으로 웹소켓의 요청을 수행하게 해준다.</li>
<li>SocketJS를 사용할 경우, 클라이언트에서 웹소켓 요청을 보낼 때 설정한 엔드포인트 뒤에 /websocket을 추가해줘야 한다고 함.</li>
</ul>
<p>WebSocket은 단순한 통신 구조이기 때문에 메세지가 어떤 요청인지, 어떻게 처리해야 하는지에 대한 정보가 없음.</p>
<p>이 정보를 담아주는 프로토콜이 <strong>STOMP 프로토콜</strong></p>
<p>클라이언트와 서버가 전송할 메세지의 유형, 형식, 내용 등을 정의한다.</p>
<p><strong>STOMP는 구독-발행의 구조</strong></p>
<p>DestinationPrefix(”/pub”)는 클라이언트에서 소켓에 메세지를 보낼 때 주소에 붙여야 하는 prefix</p>
<p>Broker(”/sub”)는 클라이언트에서 publish를 통해 메세지를 보내면 subscribe에 구독(연결 요청)을 했던 사용자들에게 메세지를 보냄</p>
<ul>
<li>프론트 예시</li>
</ul>
<pre><code>**구독**

```jsx
function connect() {
    var socket = new WebSocket(&#39;/ws&#39;);
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function () {
        setConnected(true);
        stompClient.subscribe(&#39;/sub/rooms/5&#39;, function (greeting) {
            console.log(greeting.body);
        });
    });
}
```

**발행**

```jsx
function sendMessage() {
    stompClient.send(&quot;/pub/rooms/5/chat&quot;, {}, JSON.stringify({
        &#39;message&#39;: $(&quot;#name&quot;).val(),
        &#39;senderId&#39;: 7,
        &#39;receiverId&#39;: 14,
        &#39;roomId&#39;: 5
    }));
}
```</code></pre><p><strong>Controller</strong></p>
<pre><code class="language-java">@RestController
@RequiredArgsConstructor
public class SocketController {

    private final SocketService socketService;
    @MessageMapping(&quot;/rooms/{roomId}/chat&quot;)
    @SendTo(&quot;/sub/rooms/{roomId}&quot;)
    public ChatResponse chat(@DestinationVariable Long roomId, ChatRequest request) {
        return ChatResponse.builder()
                .chatType(request.getChatType())
                .roomId(request.getRoomId())
                .userId(request.getUserId())
                .message(request.getMessage())
                .time(LocalDateTime.now())
                .build();
    }
}</code></pre>
<p><strong>@MessageMapping</strong> : DestinationPrefix와 결합해서 클라이언트가 SEND할 수 있는 주소가 된다. (&quot;/pub/rooms/{roomId}/chat”)
<strong>@DestinationVariable</strong> : {roomId}처럼 목적지의 pathvariable 변수를 받는 용도</p>
<h2 id="테스트">테스트</h2>
<p>Postman에서도 웹소켓을 지원한다고 해서 테스트를 해보려 했으나 연결이 되지 않았다. 찾아보니 웹소켓, socketIO는 지원하지만 STOMP 프로토콜은 지원하지 않기 때문이었다. (raw 파일로 STOMP 설정을 다 작성하면 복잡하지만 가능은 하다고 함...)
따라서 STOMP 테스트를 지원하는 APIC이라는 웹 앱에서 테스트를 진행했다.
<img src="https://velog.velcdn.com/images/cherry_031/post/a36f2ab2-a25a-40f0-b0f3-dfb94976ee34/image.png" alt="">
APIC의 화면이다. ws으로 new tab을 열고 Connection Type을 STOMP으로 설정해주면 된다.</p>
<p>Request URL에 웹소켓 handshake 주소를 입력하고 Connect를 클릭하면 소켓에 연결이 된다.</p>
<p><img src="https://velog.velcdn.com/images/cherry_031/post/eee4f11f-2f5f-4d13-afd2-194262c5cf39/image.png" alt="">
두 개의 APIC 창을 열어서 같은 소켓에 연결하고, 한 창에서 메세지를 보냈을 때 두 개의 창 모두 메세지를 받는 것을 확인할 수 있었다!
단, SockJS는 보안 이슈로 Origin 설정에 와일드카드를 허용하지 않는데, APIC에서는 Origin을 알 수 없어 EPIC 테스트 시에는 withSockJS();를 주석처리 해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot RestClient]]></title>
            <link>https://velog.io/@cherry_031/Spring-Boot-RestClient</link>
            <guid>https://velog.io/@cherry_031/Spring-Boot-RestClient</guid>
            <pubDate>Fri, 08 Mar 2024 10:00:17 GMT</pubDate>
            <description><![CDATA[<p>Spring Boot에서 외부 API를 호출할 수 있는 방법을 찾던 중, 기존에 많이 쓰이던 방법인 RestTemplate과 WebClient를 알게 되었다.</p>
<h3 id="resttemplate">RestTemplate</h3>
<p>RestTemplate은 스프링 프레임워크에서 제공하는 HTTP 클라이언트 라이브러리로, RESTful API를 호출할 수 있다.</p>
<ul>
<li>RestTemplate은 기본적으로 동기적인 방식으로 동작하여 요청을 보내고 응답을 받을 때까지 대기한다. 또한 Blocking I/O를 사용하여 스레드를 차단하는 방식으로 동작한다.</li>
<li>RestTemplate은 요청당 하나의 스레드를 사용한다. 따라서 많은 요청이 동시에 발생할 경우 많은 수의 스레드가 필요하다.</li>
<li>다양한 HTTP 메서드(GET, POST, PUT, DELETE 등)를 지원하며, 요청 및 응답을 쉽게 변환할 수 있는 메시지 컨버터를 제공한다.</li>
</ul>
<h3 id="webclient">WebClient</h3>
<p>WebClient는 Spring 5부터 도입된 비동기적이고 리액티브한 HTTP 클라이언트 라이브러리이다.</p>
<ul>
<li>WebClient는 비동기적인 방식으로 동작하여 요청을 보낸 후 응답을 기다리지 않고 다른 작업을 수행할 수 있다. 또한 Non-Blocking I/O를 사용하여 스레드를 차단하지 않고 요청을 처리하는 장점을 가지고 있다.</li>
<li>WebClient는 리액티브 프로그래밍 모델을 지원해, 요청과 응답을 스트림으로 처리하고 비동기적으로 데이터를 처리할 수 있는 장점을 제공한다. Spring WebFlux와 함께 사용하면 높은 성능과 확장성을 얻을 수 있다.</li>
<li>다양한 HTTP 메서드(GET, POST, PUT, DELETE 등)를 지원하며, 요청 및 응답을 쉽게 변환할 수 있는 메시지 컨버터를 제공한다. 또한, 필터를 사용하여 요청과 응답을 수정하거나 로깅할 수도 있다.</li>
</ul>
<p>위 두 가지를 공부하다가 Spring Boot 3.2.0 에서 추가된 RestClient에 대해 알게 되어 공부하였다.</p>
<h3 id="restclient">RestClient</h3>
<p>RestClient는 WebClient와 비슷한 방식으로 동작하는 HTTP 클라이언트 라이브러리이다. 다만, Spring MVC 환경에서도 사용 가능해 WebClient와 달리 WebFlux 의존성을 추가하지 않아도 되는 장점이 있으며, RestTemplate이 직관적이지 못했던 점을 개선하여 더 편리하게 사용할 수 있다.
또, 기존의 메시지 컨버터, 요청 팩토리, 인터셉터 및 RestTemplate의 다른 구성 요소를 그대로 사용할 수 있다.</p>
<p>이번에 RestClient를 사용하여 외부 서버에 API 요청을 보내는 작업을 해보고 있다.
최근에 나온 라이브러리라 레퍼런스는 별로 없지만 최신 라이브러리를 사용해 볼 수 있는 경험이라 좋을 것 같다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 7586]]></title>
            <link>https://velog.io/@cherry_031/%EB%B0%B1%EC%A4%80-7586</link>
            <guid>https://velog.io/@cherry_031/%EB%B0%B1%EC%A4%80-7586</guid>
            <pubDate>Wed, 20 Sep 2023 12:12:37 GMT</pubDate>
            <description><![CDATA[<h4 id="처음-사용한-방법-틀렸습니다">처음 사용한 방법 (틀렸습니다!)</h4>
<p>각각 덩치가 큰 사람의 수를 리스트에 저장해 덩치 등수를 한 번에 출력</p>
<pre><code>peopleNum = int(input())
stats = [tuple(map(int, input().split())) for _ in range(peopleNum)]

biggerCount = [0] * peopleNum  # 나보다 덩치가 큰 사람의 수
for first in stats:
    for second in stats[stats.index(first)+1:]:
        if first[0] &gt; second[0] and first[1] &gt; second[1]:
            biggerCount[stats.index(second)] += 1
        elif first[0] &lt; second[0] and first[1] &lt; second[1]:
            biggerCount[stats.index(first)] += 1

# 자신보다 더 큰 덩치의 사람이 k명이라면 그 사람의 덩치 등수는 k+1
print(&quot; &quot;.join([str(c+1) for c in biggerCount]))
</code></pre><p>질문 게시판 반례 적용해봐도 다 되는데 계속 틀렸습니다! 가 떠서 화난 상태에서...
덩치 등수를 한 사람마다 비교가 모두 끝나면 출력하는 방식으로 변경해보았다.</p>
<h4 id="두-번째-방법-맞았습니다">두 번째 방법 (맞았습니다!)</h4>
<pre><code>peopleNum = int(input())
stats = [tuple(map(int, input().split())) for _ in range(peopleNum)]

for first in stats: 
    biggerCount = 0  # 나보다 덩치가 큰 사람의 수 
    for second in stats:
        if first[0] &lt; second[0] and first[1] &lt; second[1]:
            biggerCount += 1

    # 자신보다 더 큰 덩치의 사람이 k명이라면 그 사람의 덩치 등수는 k+1
    print(biggerCount+1, end=&#39; &#39;)
</code></pre><p>맞았습니다! 를 받았지만 도저히 두 코드의 논리적 차이가 눈에 보이지 않았다.
그러다 결국 찾아낸 부분...</p>
<blockquote>
<pre><code>biggerCount[stats.index(second)] += 1</code></pre></blockquote>
<pre><code>
list의 index 메소드는 가장 처음으로 발견되는 item의 인덱스를 반환한다는 것...ㅠㅠㅠ
중복되는 값이 있으면 계속 제일 왼 쪽에 있는 친구의 카운트만 증가하고 있었던 거다ㅠㅠ

#### 첫 번째 방법 반례</code></pre><p>INPUT
5
30 30
50 50
40 20
30 30
20 20</p>
<p>OUTPUT
4 1 2 1 4</p>
<p>ANSWER
2 1 2 2 4 </p>
<pre><code>
애초에 ```for first in stats:``` 부분을 ```for idx in range(len(stats)):```로만 했어도...

풀이 방법도 엄청 쉬웠는데 찾는 데 오래 걸리고, 이 사소한 디버깅도 오래 걸렸던 문제...
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 입출력 방법]]></title>
            <link>https://velog.io/@cherry_031/C-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@cherry_031/C-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 18 Sep 2023 16:28:31 GMT</pubDate>
            <description><![CDATA[<h2 id="1-cin-cout">1. cin, cout</h2>
<p>c++ 표준 입출력</p>
<pre><code>#include &lt;iostream&gt;

// using namespace std;

int main() {
    int a;
    std::cin &gt;&gt; a;
    std::cout &lt;&lt; &quot;Hello&quot;;
    return 0;
}</code></pre><br/>

<h2 id="2-scanf-printf">2. scanf(), printf()</h2>
<p>cin, cout 보다 속도가 빨라 백준, 코테 등에서 쓰기 좋은 입출력 방법
데이터 형식을 지정해야 함(형식지정자)</p>
<pre><code>#include &lt;cstdio&gt;

int main() {
    int intNum;
    double floatNum;
    scanf(&quot;%d %f&quot;, &amp;intNum, &amp;floatNum);
    printf(&quot;출력: %d, %f\n&quot;, intNum, floatNum);
    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[파이썬 리스트 함수]]></title>
            <link>https://velog.io/@cherry_031/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@cherry_031/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%ED%95%A8%EC%88%98</guid>
            <pubDate>Mon, 18 Sep 2023 13:50:06 GMT</pubDate>
            <description><![CDATA[<h2 id="맨날-헷갈려서-맨날-찾아보는-문법-시리즈">맨날 헷갈려서 맨날 찾아보는 문법 시리즈</h2>
<br/>


<h3 id="파이썬-list-관련-함수-및-메소드">파이썬 list 관련 함수 및 메소드</h3>
<h4 id="정보">정보</h4>
<ul>
<li><p>len(list)
list의 길이 반환</p>
</li>
<li><p>list.index(item)
item의 인덱스 반환</p>
</li>
<li><p>list.count(item)
리스트에 포함된 item 개수 반환</p>
</li>
<li><p>list.clear()
리스트의 모든 요소 삭제</p>
</li>
<li><p>max(list)
리스트 내 최대 요소 반환</p>
</li>
<li><p>min(list)
리스트 내 최소 요소 반환</p>
</li>
<li><p>sum(list)
리스트의 모든 요소의 합 반환</p>
</li>
<li><p>all(list)
리스트의 모든 요소가 True이면 True 반환</p>
</li>
<li><p>any(list)
리스트의 요소 중 하나라도 True이면 True 반환</p>
</li>
</ul>
<h4 id="추가">추가</h4>
<ul>
<li><p>list.append(item)
리스트 맨 뒤에 item 추가</p>
</li>
<li><p>list.insert(index, item)
item을 index번째에 추가</p>
</li>
<li><p>list1.extend(list2)
list1의 마지막에 list2를 추가</p>
</li>
</ul>
<h4 id="삭제">삭제</h4>
<ul>
<li><p>list.remove(item)
리스트에서 가장 왼쪽에 있는 item 삭제</p>
</li>
<li><p>del list[index] or del(list[index])
리스트에서 index의 요소 삭제
인덱스 슬라이싱으로 여러 요소 한 번에 삭제 가능
리스트 자체 삭제 가능</p>
</li>
<li><p>list.pop(index)
리스트에서 index의 요소를 삭제하면서 그 값을 반환
index 미입력시 마지막 요소 삭제 후 그 값을 반환</p>
</li>
</ul>
<h4 id="정렬">정렬</h4>
<ul>
<li><p>list.reverse()
리스트 자체를 역순으로 정렬</p>
</li>
<li><p>reversed(list)
리스트를 역순으로 정렬한 리스트를 반환</p>
</li>
<li><p>list.sort()
리스트 자체를 오름차순으로 정렬
내림차순 정렬은 list.sort(reverse=True)</p>
</li>
<li><p>sorted(list)
리스트를 오름차순으로 정렬한 리스트를 반환
내림차순 정렬은 sorted(list, reverse=True)</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[파이썬 join 함수 / 삼항 연산자]]></title>
            <link>https://velog.io/@cherry_031/%ED%8C%8C%EC%9D%B4%EC%8D%AC-join-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@cherry_031/%ED%8C%8C%EC%9D%B4%EC%8D%AC-join-%ED%95%A8%EC%88%98</guid>
            <pubDate>Mon, 18 Sep 2023 12:22:49 GMT</pubDate>
            <description><![CDATA[<h2 id="맨날-헷갈려서-맨날-찾아보는-문법-시리즈">맨날 헷갈려서 맨날 찾아보는 문법 시리즈</h2>
<br/>

<h3 id="join-함수-사용법">join 함수 사용법</h3>
<p><strong>&#39;구분자&#39;.join(리스트)</strong></p>
<pre><code>print(&#39;, &#39;.join([&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]))  // a, b, c</code></pre><p>문자열로 반환하기 때문에 리스트의 요소가 str이어야 함~
<br/></p>
<p>맨!날! 순서 헷갈리는 조인 함수..
구분자, 조인, 리스트!!
<br/><br/></p>
<h3 id="삼항-연산자">삼항 연산자</h3>
<h4 id="not-python">not python</h4>
<pre><code>(조건식) ? (참 value) : (거짓 value)</code></pre><h4 id="python">python</h4>
<pre><code>(참 value) if (조건식) else (거짓 value)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[23.09.18 C++ Tutorial]]></title>
            <link>https://velog.io/@cherry_031/23.09.18-C-Tutorial-owep6txa</link>
            <guid>https://velog.io/@cherry_031/23.09.18-C-Tutorial-owep6txa</guid>
            <pubDate>Sun, 17 Sep 2023 17:39:38 GMT</pubDate>
            <description><![CDATA[<ul>
<li>C++ Tutorial for Beginners - Learn C++ in 1 Hour (<a href="https://www.youtube.com/watch?v=ZzaPdXTrSb8&amp;t=617s">https://www.youtube.com/watch?v=ZzaPdXTrSb8&amp;t=617s</a>) 보며 공부하기<br/>

</li>
</ul>
<h3 id="자료형">자료형</h3>
<p><img src="https://velog.velcdn.com/images/cherry_031/post/d0ef0108-86cd-4b8d-8950-6dcec07e782c/image.png" alt=""></p>
<p><em>출처: <a href="https://sweetnew.tistory.com/16#google_vignette">https://sweetnew.tistory.com/16#google_vignette</a></em></p>
<br/>

<p>float형 변수에 값을 할당할 때는 반드시 f나 F를 붙여야 함
<em>why?</em></p>
<ol>
<li>f가 없는 실수는 기본적으로 double형으로 처리되기 때문에 double 값을 float 변수에 넣는 과정에서 data loss로 인한 오차 발생</li>
<li>auto를 사용했을 때 의도대로 변수 타입이 지정되도록 하기 위함</li>
</ol>
<p>마찬가지로, long형 변수에 값을 할당할  때는 l이나 L을 붙여야 함</p>
<pre><code>(...)

int main() {
    double price = 99.99;
    float interestRate = 3.67f;
    long fileSize = 90000L;
    char letter = &#39;a&#39;;
    bool isValid = true;
    auto isValid = true;  // auto는 할당된 값에 따라 자동으로 변수 타입을 정해줌
    return 0;
}</code></pre><br/>

<h3 id="유니폼-초기화---사용-">유니폼 초기화 ( {} 사용 )</h3>
<ul>
<li>변수 타입에 맞지 않는 값이 할당되는 상황을 방지할 수 있음</li>
<li>괄호 안에 값이 없으면 자동으로 기본값으로 초기화 해줌</li>
</ul>
<pre><code>(...)

int main() {
    // 캐스팅 방지
    int number = 1.2;
    cout &lt;&lt; number;  // 1

    // int number {1, 2}  =&gt;  Error!


    // 자동 초기화
    int number;
    cout &lt;&lt; number; // random value(garbage)

    int number {};
    cout &lt;&lt; number;  // 0

    return 0;
}</code></pre><br/>

<h3 id="진법-표현">진법 표현</h3>
<pre><code>(...)

int main() {
    int number = 0b11111111;  // 2진법
    cout &lt;&lt; number;  // 255

    int number = 0xFF;  // 16진법
    cout &lt;&lt; number;  // 255

    return 0;
}</code></pre><br/>

<h3 id="난수-생성">난수 생성</h3>
<ul>
<li>cstdlib의 rand() 함수 사용
프로그램을 여러 번 실행해도 처음 생성된 난수 값만 나옴<pre><code>#include &lt;iostream&gt;
#include &lt;cstdlib&gt;
</code></pre></li>
</ul>
<p>using namespace std;</p>
<p>int main() {
    int number = rand();
    cout &lt;&lt; number;  // 프로그램을 여러 번 실행해도 같은 값이 나옴
    return 0;
}</p>
<pre><code>
- srand() 함수 사용
srand()의 파라미터 값이 달라지면 값이 달라짐
단, 파라미터 값이 같으면 항상 같은 값이 나옴</code></pre><p>#include <iostream>
#include <cstdlib></p>
<p>using namespace std;</p>
<p>int main() {
    srand(5);
    int number = rand();
    cout &lt;&lt; number;  // srand(5)이면 프로그램을 여러 번 실행해도 같은 값이 나옴
    return 0;
}</p>
<pre><code>

- ctime의 time() 함수 사용
seed가 달라 항상 다른 난수 생성됨</code></pre><p>#include <iostream>
#include <cstdlib>
#include <ctime></p>
<p>using namespace std;</p>
<p>int main() {
    long elapsedSeconds = time(nullptr);  // Jan 1 1970 으로부터 현재까지 지난 초의 수
    srand(elapsedSeconds);
    int number = rand();
    cout &lt;&lt; number;
    return 0;
}</p>
<p>```</p>
<hr>
<p>C++ Tutorial 동영상 끝!! 🥰😍</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.09.17 C++ Tutorial]]></title>
            <link>https://velog.io/@cherry_031/23.09.17-C-Tutorial</link>
            <guid>https://velog.io/@cherry_031/23.09.17-C-Tutorial</guid>
            <pubDate>Sun, 17 Sep 2023 15:04:52 GMT</pubDate>
            <description><![CDATA[<ul>
<li>C++ Tutorial for Beginners - Learn C++ in 1 Hour (<a href="https://www.youtube.com/watch?v=ZzaPdXTrSb8&amp;t=617s">https://www.youtube.com/watch?v=ZzaPdXTrSb8&amp;t=617s</a>) 보며 공부하기<br/>

</li>
</ul>
<h3 id="naming-convention">Naming Convention</h3>
<pre><code>int file_size; // Snake Case
int FileSize; // Pascal Case
int fileSize; // Camel Case
int iFileSize; // Hungarian Notation (not relevant anymore)</code></pre><br/>

<h3 id="console-출력">console 출력</h3>
<pre><code>(...)

int main() {
    int x = 10;
    int y = 20;

    // #1
    cout &lt;&lt; &quot;x = &quot; &lt;&lt; x &lt;&lt; endl;
    cout &lt;&lt; &quot;y = &quot; &lt;&lt; y;

    // #2
    cout &lt;&lt; &quot;x = &quot; &lt;&lt; x &lt;&lt; endl
         &lt;&lt; &quot;y = &quot; &lt;&lt; y;

    return 0;
}</code></pre><br/>

<h3 id="console-입력">console 입력</h3>
<p>입력은 줄바꿈이나 공백(개수 상관 없음)으로 구분됨
10\n20 or 10 20 입력 ==&gt; x=10, y=20</p>
<pre><code>(...)

int main() {
    double x;
    double y;

    // #1
    cin &gt;&gt; x;
    cin &gt;&gt; y;

    // #2
    cin &gt;&gt; x &gt;&gt; y;

    return 0;
}</code></pre><br/>

<h3 id="math-library">math library</h3>
<pre><code>#include &lt;iostream&gt;
#include &lt;cmath&gt;

using namespace std;

int main() {
    double result1 = floor(1.2);
    cout &lt;&lt; result1;  // 1

    double result2 = pow(2, 3);
    cout &lt;&lt; result2;  // 8

    return 0;
}</code></pre><br/>

<h3 id="comment">comment</h3>
<p>주석은 남용하면 좋지 않음
<strong><em>only to explain whys and how
not what</em></strong></p>
<pre><code>#include &lt;iostream&gt;

using namespace std;

int main() {
    // 이것은 주석입니다.
    // 슬래시 2개!

    /*
    여러 줄 주석
    (/* */)
    */

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[파이썬 문자열 출력]]></title>
            <link>https://velog.io/@cherry_031/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%B6%9C%EB%A0%A5</link>
            <guid>https://velog.io/@cherry_031/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%B6%9C%EB%A0%A5</guid>
            <pubDate>Sun, 09 Jul 2023 12:17:41 GMT</pubDate>
            <description><![CDATA[<h2 id="맨날-헷갈려서-맨날-찾아보는-문법-시리즈">맨날 헷갈려서 맨날 찾아보는 문법 시리즈</h2>
<br/>

<pre><code>안녕하세요.
반갑습니다.
잘 부탁드립니다.</code></pre><p>라는 문자열을 출력하고 싶을 때 사용할 수 있는 방법들</p>
<h3 id="줄-별-출력">줄 별 출력</h3>
<pre><code>print(&quot;안녕하세요.&quot;)
print(&quot;반갑습니다.&quot;)
print(&quot;잘 부탁드립니다.&quot;)
</code></pre><h3 id="한-번에-출력문자-그대로">한 번에 출력(문자 그대로)</h3>
<pre><code>print(&quot;&quot;&quot;안녕하세요.
반갑습니다.
잘 부탁드립니다.&quot;&quot;&quot;)</code></pre><h3 id="한-번에-출력역슬래시">한 번에 출력(역슬래시)</h3>
<pre><code>print(&quot;&quot;&quot;\
안녕하세요.
반갑습니다.
잘 부탁드립니다.\
&quot;&quot;&quot;)</code></pre><p>역슬래시를 쓰지 않으면 문자열 처음과 끝에 엔터가 생긴다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.01.17 C++ Tutorial]]></title>
            <link>https://velog.io/@cherry_031/23.01.17-C-Tutorial</link>
            <guid>https://velog.io/@cherry_031/23.01.17-C-Tutorial</guid>
            <pubDate>Tue, 17 Jan 2023 08:50:42 GMT</pubDate>
            <description><![CDATA[<ul>
<li>C++ Tutorial for Beginners - Learn C++ in 1 Hour (<a href="https://www.youtube.com/watch?v=ZzaPdXTrSb8&amp;t=617s">https://www.youtube.com/watch?v=ZzaPdXTrSb8&amp;t=617s</a>) 보며 공부하기</li>
</ul>
<p> #</p>
<p>정수 타입 <strong>int</strong>
실수 타입 <strong>double</strong></p>
<pre><code>#include &lt;iostream&gt;

int main() {
    int file_size = 100;
    double sales = 9.99;

    std::cout &lt;&lt; file_size;  // 100

    return 0;
}</code></pre><p>#</p>
<p>std:: 를 생략하기 위해 <code>using namespace std;</code>를 써준다.</p>
<p>변수를 상수로 설정하는 키워드 <strong>const</strong>
타입 지정 키워드 앞에 써준다.</p>
<pre><code>#include &lt;iostream&gt;

using namespace std;

int main() {
    const double pi = 3.14;
    // pi = 0;  =&gt;  Error!

    return 0;
}</code></pre>]]></description>
        </item>
    </channel>
</rss>