<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>컴퓨터가 뭘까</title>
    <link>https://yeondube.tistory.com/</link>
    <description>정리초보 끄적끄적</description>
    <language>ko</language>
    <pubDate>Mon, 10 Aug 2026 02:57:13 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>연두연두부</managingEditor>
    <image>
      <title>컴퓨터가 뭘까</title>
      <url>https://tistory1.daumcdn.net/tistory/3573422/attach/133e7917422e4f04b7b3ac5ffa5e9092</url>
      <link>https://yeondube.tistory.com</link>
    </image>
    <item>
      <title>Kotlin Spring boot에서 AWS Secret Manager 사용하기</title>
      <link>https://yeondube.tistory.com/entry/Kotlin-Spring-boot%EC%97%90%EC%84%9C-AWS-Secret-Manager-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Secrets Manager는 데이터베이스, API 키, 토큰 등 secret 정보를 안전하게 저장하고 교체 및 관리할 수 있게 해주는 AWS 서비스입니다. 코드 상에 보안 유지가 필요한 정보를 노출하여 공개 원격저장소에 올라가게 된다면? 더 나아가 서비스 코드일 경우는 그 영향은 치명적일 것입니다. 민감한 정보들을 관리하는 다양한 솔루션들이 있습니다. 그 중에 AWS Secrets Manager을 소개합니다. 본 글은 AWS RDS를 사용하는 것을 전제로 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS에서 세팅하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 서비스 중 Secrets Manager를 이동합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;1118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqzLcW/btsGfg8RU8m/XovKo0ypja5oWOhyf05CZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqzLcW/btsGfg8RU8m/XovKo0ypja5oWOhyf05CZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqzLcW/btsGfg8RU8m/XovKo0ypja5oWOhyf05CZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqzLcW%2FbtsGfg8RU8m%2FXovKo0ypja5oWOhyf05CZ0%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;1778&quot; height=&quot;1118&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;1118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목록 우측 상단에 버튼을 클릭하여 신규로 생성합니다. Secret Type은 RDS를 선택하고 username과 secret name을 동일하게 /dev/practice/mysql로 하여 만들었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2718&quot; data-origin-height=&quot;1694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn8feg/btsGd9pjKic/OHzBlzdovZrFsqXpxTKSXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn8feg/btsGd9pjKic/OHzBlzdovZrFsqXpxTKSXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn8feg/btsGd9pjKic/OHzBlzdovZrFsqXpxTKSXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn8feg%2FbtsGd9pjKic%2FOHzBlzdovZrFsqXpxTKSXk%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;2718&quot; height=&quot;1694&quot; data-origin-width=&quot;2718&quot; data-origin-height=&quot;1694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;encryption key는 AWS KMS로 미리 만들어둔 aws/secretsmanager를 사용하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2734&quot; data-origin-height=&quot;1532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kXB2H/btsGfhmoTY6/7S0wlcNqcGFpnVDLian0C1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kXB2H/btsGfhmoTY6/7S0wlcNqcGFpnVDLian0C1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kXB2H/btsGfhmoTY6/7S0wlcNqcGFpnVDLian0C1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkXB2H%2FbtsGfhmoTY6%2F7S0wlcNqcGFpnVDLian0C1%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;2734&quot; height=&quot;1532&quot; data-origin-width=&quot;2734&quot; data-origin-height=&quot;1532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Secret 생성 후에 Secret Value를 보고 수정할 수 있습니다. 다음 secret 에서&amp;nbsp;&lt;u&gt;username, password, engine, host, port, dbname&lt;/u&gt; 관련 secret value를 관리하고 있는 것을 확인할할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3414&quot; data-origin-height=&quot;1688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1ggUe/btsGgCp32Td/D6Izg2ejRXrsoBLErAKsHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1ggUe/btsGgCp32Td/D6Izg2ejRXrsoBLErAKsHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1ggUe/btsGgCp32Td/D6Izg2ejRXrsoBLErAKsHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1ggUe%2FbtsGgCp32Td%2FD6Izg2ejRXrsoBLErAKsHK%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;3414&quot; height=&quot;1688&quot; data-origin-width=&quot;3414&quot; data-origin-height=&quot;1688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring boot에 적용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring boot에서 secret manager를 사용하고자 build.gradle.kts에 다음 dependency를 추가하여 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1711898078559&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// aws secret manager 관련
implementation(&quot;software.amazon.awssdk:secretsmanager&quot;)

// (선택) bootstrap을 사용한다면 추가
implementation(&quot;org.springframework.cloud:spring-cloud-starter-bootstrap:4.1.2&quot;)

// json 관련
implementation(&quot;com.google.code.gson:gson:2.10.1&quot;)

// (선택) aws secret manager에서 제공하는 jdbc 사용한다면 추가
implementation(&quot;com.amazonaws.secretsmanager:aws-secretsmanager-jdbc:1.0.8&quot;)

// (선택) app 자체적으로 mysql jdbc 관리 등 목적으로 사용한다면 추가
implementation(&quot;com.mysql:mysql-connector-j:8.3.0&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 첫번째 방식으로 AWS secrets manager에서 secret value를 가져올 수 있습니다. 이 때 필요한 dependency입니다&lt;/p&gt;
&lt;pre id=&quot;code_1711956422255&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// aws secret manager 관련
implementation(&quot;software.amazon.awssdk:secretsmanager&quot;)

implementation(&quot;org.springframework.cloud:spring-cloud-starter-bootstrap:4.1.2&quot;)

implementation(&quot;com.amazonaws.secretsmanager:aws-secretsmanager-jdbc:1.0.8&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1711956352145&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# application-dev.yml
spring:
  datasource:
    url: jdbc-secretsmanager:mysql://{db-url}
    username: dev/practice/mysql
    driver-class-name: com.amazonaws.secretsmanager.sql.AWSSecretsManagerMySQLDriver
...

# application.yml
cloud:
  aws:
    region:
      static: ap-northeast-2
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Secrets Manager에서 MySQL 외 다른 데이터베이스도 지원하고 있습니다. &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/secretsmanager/latest/userguide/retrieving-secrets_jdbc.html#retrieving-secrets_jdbc_example_c3po&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;AWS Secrets Manager 공식문서&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711956795925&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;AWS Secrets Manager 보안 암호의 자격 증명을 사용하여 SQL 데이터베이스에 연결 - AWS Secrets Manager&quot; data-og-description=&quot;기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다. AWS Secrets Manager 보안 암호의 자격 증명을 사용하여 SQL 데이터베이스에 연결&quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/ko_kr/secretsmanager/latest/userguide/retrieving-secrets_jdbc.html#retrieving-secrets_jdbc_example_c3po&quot; data-og-url=&quot;https://docs.aws.amazon.com/ko_kr/secretsmanager/latest/userguide/retrieving-secrets_jdbc.html#retrieving-secrets_jdbc_example_c3po&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/secretsmanager/latest/userguide/retrieving-secrets_jdbc.html#retrieving-secrets_jdbc_example_c3po&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/ko_kr/secretsmanager/latest/userguide/retrieving-secrets_jdbc.html#retrieving-secrets_jdbc_example_c3po&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;AWS Secrets Manager 보안 암호의 자격 증명을 사용하여 SQL 데이터베이스에 연결 - AWS Secrets Manager&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다. AWS Secrets Manager 보안 암호의 자격 증명을 사용하여 SQL 데이터베이스에 연결&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 두번째 방식은 app 레벨에서 Aws Secrets Manager secret 정보를 가져오는 부분을 직접 만들어 커스텀하게 사용할 수 있습니다. 이번 글에서는 직접 구현해서 secret 정보를 사용할 예정입니다. 이 떄 dependency입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1711947276041&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// aws secret manager 관련
implementation(&quot;software.amazon.awssdk:secretsmanager&quot;)

// json 관련
implementation(&quot;com.google.code.gson:gson:2.10.1&quot;)

// mysql jdbc 관련
implementation(&quot;com.mysql:mysql-connector-j:8.3.0&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이번 포스트에서는 직접 Secret Value를 들고오는 로직을 구현해서 datasource를 만드는 예제를 진행할 예정입니다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로직 직접 구현해서 Secret 정보&amp;nbsp;사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 팀에서 프로젝트 진행 시 local과 dev, stage, prod(product) 크게 2가지로 나눠 database config를 구성하고 있습니다. 그 이유는 보안중요성이 다르기 때문입니다. 즉, aws secrets manager에는 dev, stage, prod(product)에 대응하는 db secret 정보를 관리하고 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 profile(local, dev, stage, prod)이 공통으로 사용하는 application 정보는 application.yml에서 관리하고 있습니다.&amp;nbsp; datasource 연결 시 mysql driver를 사용하기 때문에 application.yml에 명시해서 사용하고 있습니다. 그리고 현재 AWS 서비스를 ap-northeast-2(Seoul) region에서 사용하고 있기 때문에 region을 명시합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1711949081891&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# application.yml
spring:
    datasource:
        hikari:
            connection-init-sql: SELECT 1
            driver-class-name: com.mysql.cj.jdbc.Driver
    ...

cloud:
  aws:
    region:
      static: ap-northeast-2&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;로컬환경(즉,profile이 local)에서 개발 시 유연하게 대처하기 위해 application-local.yml에 직접 db url, username, password를 명시해서 사용합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1711949081892&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# application-local.yml
spring:
  datasource:
    hikari:
      local:
        jdbc-url: jdbc:mysql://{my-host}:{my-port}/{my-dbname}?useSSL=false&amp;amp;rewriteBatchedStatements=true&amp;amp;profileSQL=true&amp;amp;logger=Slf4JLogger&amp;amp;maxQuerySizeToLog=999999
        username: {my-username}
        password: {my-password}
  ...&lt;/code&gt;&lt;/pre&gt;
&lt;p 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;이외 profile(dev, stage, prod)에서 각 profile에 대응하는 application-xxx.yml에, profile에 대응하는 AWS Secrets Manager에 저장한 dbSecretKey를 명시합니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# application-dev.yml
cloud:
  aws:
    secretsManager:
      dbSecretKey: dev/practice/mysql&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 글 예제는 profile &lt;b&gt;dev&lt;/b&gt; 기준으로, 앞서 만든 AWS RDS secret 정보를 AWS Secrets Manager에서 불러와서 datasource를 만들어 사용하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Aws Secret 컴포넌트에서 application.yml에 명시한 region에 있는 aws secrets manager client를 관리합니다. 그리고 secret client를 통해 secretName에 대응하는 secret 정보를 찾아 secret value를 가져오는 역할을 수행합니다. 이 때 발생하는 exception은 aws secrets manager 관련 exception으로 specific한 exception을 사용했습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Component
class AwsSecret : LoggerCreator() {

    @Value(&quot;\${cloud.aws.region.static}&quot;)
    private lateinit var region: String

    @Throws(AWSSecretsManagerException::class)
    fun secretManagerClient(): SecretsManagerClient =
        SecretsManagerClient.builder()
            .region(Region.of(region))
            .credentialsProvider(DefaultCredentialsProvider.create())
            .build()

    @Throws(AWSSecretsManagerException::class)
    fun secretValue(secretName: String): String =
        secretManagerClient().getSecretValue(
            GetSecretValueRequest.builder()
                .secretId(secretName)
                .build()
        ).secretString()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AwsAdapter 컴포넌트에서는 secret Name에 대응하는 secret value을 AwsSecret 컴포넌트를 통해 가져옵니다. 이 때 secret Value는 Json text 형태로, AwsAdapter 컴포넌트에서 gson 을 통해 dto 객체로 변환합니다. 그리고 AWS Secrets Manager에서 secret value을 가져오면서 발생한 exception을 처리합니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;@Component
class AwsAdapter(
    private val awsSecret: AwsSecret
) : LoggerCreator() {
    fun getDbSecretValue(secretName: String): DbSecretValueDto? =
        try {
            GsonUtil.fromJson(awsSecret.secretValue(secretName), DbSecretValueDto::class.java)
        } catch (e: Exception) {
            log.error(FAIL_TO_GET_DB_SECERT_VALUE, e)
            null
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AwsAdapter 컴포넌트에서 변환하는 DbSecretValueDto입니다. gson util에서 singleton &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Gson 객체를&lt;span&gt; 이용하여 gson 에서 지원하는 fromJson을 사용할 수 있도록 하였습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;data class DbSecretValueDto(
    val username: String?,
    val password: String?,
    val engine: String?,
    val host: String?,
    val port: String?,
    val dbname: String?
)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1711947148051&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;object GsonUtil {
    private val gson = GsonBuilder()
        .disableHtmlEscaping()
        .setPrettyPrinting()
        .serializeNulls()
        .create()

    fun &amp;lt;T&amp;gt; fromJson(json: String, classOfT: Class&amp;lt;T&amp;gt;): T = gson.fromJson(json, classOfT)
}&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;Database config에서 local 외 profile일 경우 (dev, stage, prod) AwsAdapter을 통해 DB Secret Value 정보 DbSecretValueDto를 활용하여 datasource를 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;@Configuration
class DatabaseConfig (
    private val awsAdapter: AwsAdapter
): LoggerCreator() {
    @Value(&quot;\${cloud.aws.secretsManager.dbSecretKey}&quot;)
    private lateinit var secretName: String

    /**
     * local 외 DataSource bean 생성 함수
     */
    @Primary
    @Bean(name = [&quot;defaultDataSource&quot;])
    @Profile(&quot;dev&quot;, &quot;stg&quot;, &quot;prd&quot;)
    fun defaultDataSource(hikariProperties: HikariProperties): DataSource? = try {
        // DB 정보 획득
        val dbSecret = secretName.let {
            awsAdapter.getDbSecretValue(it)
        } ?: throw Exception(FAIL_TO_GET_VALUE_FROM_APPLICATION)

        // JDBC URL 생성
        val jdbcUrl = buildString {
            append(&quot;jdbc:mysql://&quot;)
            append(&quot;${dbSecret.host}:${dbSecret.port}/${dbSecret.dbname}&quot;)
            append(&quot;?allowUrlInLocalInfile=true&amp;amp;allowLoadLocalInfile=true&amp;amp;rewriteBatchedStatements=true&quot;)
        }

        // DataSource 생성
        hikariDataSource(HikariConfig().apply {
            this.jdbcUrl = jdbcUrl
            this.username = dbSecret.username
            this.password = dbSecret.password
        })
    } catch (var7: Exception) {
        log.error(ERROR_DATA_CONFIG, var7)
        var7.printStackTrace()
        null
    }

     /**
     * local DataSource bean 생성 함수
     */
    @Primary
    @Bean(name = [&quot;defaultDataSource&quot;])
    @Profile(&quot;local&quot;)
    fun defaultDataSourceLocal(hikariProperties: HikariProperties): DataSource = hikariDataSource(HikariConfig().apply {
        jdbcUrl = hikariProperties.local!!.jdbcUrl
        username = hikariProperties.local.username
        password = hikariProperties.local.password
    })

    private fun hikariDataSource(hikariConfig: HikariConfig) = HikariDataSource(hikariConfig).also { it.validate() }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 Spring boot프로젝트에서 Jpa를 사용한다면 transaction 관련 config를 추가해서 사용해주면 됩니다. 다음 config는 database config에서 설정된 dataSource bean을 활용합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1711899036297&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;@EnableJpaRepositories(
    basePackages = [&quot;com.study.my.*.domain&quot;],
    entityManagerFactoryRef = &quot;defaultEntityManagerFactory&quot;,
    transactionManagerRef = &quot;defaultTransactionManager&quot;
)
@Configuration
@EnableTransactionManagement
class TransactionConfig(
    @Qualifier(&quot;defaultDataSource&quot;) private val dataSource: DataSource?,
    private val appConfig: AppConfig,
    private val jpaProperties: JpaProperties,
    private val hibernateProperties: HibernateProperties
) : LoggerCreator() {

    @Value(&quot;\${my-project.config.jpa.default-base-packages:com.study.my.*.domain}&quot;)
    private lateinit var BASE_PACKAGES: String

    @Value(&quot;\${my-project.config.jpa.default-packages-to-scan:com.study.my.*.domain.*.entity}&quot;)
    private lateinit var PACKAGES_TO_SCAN: String

    @Primary
    @Bean(name = [&quot;defaultEntityManagerFactory&quot;])
    fun defaultEntityManagerFactory(
        builder: EntityManagerFactoryBuilder
    ): LocalContainerEntityManagerFactoryBean = hibernateProperties.determineHibernateProperties(
        jpaProperties.properties, HibernateSettings()
    ).let {
        builder
            .dataSource(dataSource)
            .packages(*arrayOf(PACKAGES_TO_SCAN))
            .persistenceUnit(&quot;defaultEntityManager&quot;)
            .properties(it)
            .build()
    }

    @Primary
    @Bean(name = [&quot;defaultTransactionManager&quot;])
    fun defaultTransactionManager(@Qualifier(&quot;defaultEntityManagerFactory&quot;) entityManagerFactory: EntityManagerFactory): PlatformTransactionManager =
            JpaTransactionManager(entityManagerFactory)

}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Java/Spring</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/92</guid>
      <comments>https://yeondube.tistory.com/entry/Kotlin-Spring-boot%EC%97%90%EC%84%9C-AWS-Secret-Manager-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#entry92comment</comments>
      <pubDate>Sun, 31 Mar 2024 23:59:19 +0900</pubDate>
    </item>
    <item>
      <title>kotlin 프로젝트에서 openapi code generator 사용하기</title>
      <link>https://yeondube.tistory.com/entry/kotlin-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-openapi-code-generator-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
      <description>&lt;h2&gt;서론&lt;/h2&gt;
&lt;p&gt;현재 참여하고 있는 프로젝트는 쿠버네티스 환경으로, 여러 컴포넌트로 구성된 마이크로서비스 아키텍처입니다. 각 컴포넌트는 서로 다른 네트워크 주소와 포트에서 실행됩니다. 따라서 각 컴포넌트의 API를 호출하려면 다음과 같은 어려움을 겪었습니다. 하지만 분산된 시스템에서 서로 다른 서비스의 API를 호출하는 것은 복잡합니다. 네트워크 주소 관리, 로드 밸런싱, 오류 처리 등 고려해야 할 사항이 많기 때문입니다. 이러한 과정을 자동화하고 개발 효율성을 높이기 위해서 Open Api CodeGenerator를 도입하는 것을 고려했습니다.&lt;/p&gt;
&lt;p&gt;Open Api Code Generator는 OpenAPI Spec을 기반으로 프로젝트 코드를 생성해주는 오픈소스 툴입니다. 기본적으로 각 컴포넌트 API를 OpenAPI 스펙으로 정의하여 Api문서 공유가 가능합니다. 그리고 그 스펙을 기반으로 자동으로 Api client 코드를 생성되어 Controller 클래스가 상속받아 사용하여 일관성이 있게 개발가능합니다. 그리고 다른 컴포넌트의 API도 손쉽게 호출할 수 있습니다! 이를 통해 자동으로 생성된 API 클라이언트 코드로 API 테스트를 자동화하여 API 호출 문제를 사전에 예방할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;images.png&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckBmEE/btsF02Li3G2/VBe27FoexDWk8ePoxIyuj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckBmEE/btsF02Li3G2/VBe27FoexDWk8ePoxIyuj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckBmEE/btsF02Li3G2/VBe27FoexDWk8ePoxIyuj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckBmEE%2FbtsF02Li3G2%2FVBe27FoexDWk8ePoxIyuj0%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;318&quot; height=&quot;159&quot; data-filename=&quot;images.png&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;현재 팀에서 각 컴포넌트에 대한 Api Spec 작성 및 관리는 &lt;a href=&quot;https://app.swaggerhub.com/search&quot;&gt;Swagger hub&lt;/a&gt; 통해 하고 있습니다. 이번 포스트에서는 swagger hub에 있는 Api spec을 이용하여 Api 개발 시 필요한 세팅을 작성하고자 합니다.&lt;/p&gt;
&lt;h2&gt;환경&lt;/h2&gt;
&lt;p&gt;기본 프로젝트 환경은 spring boot &lt;code&gt;2.7&lt;/code&gt;, Java Version &lt;code&gt;11&lt;/code&gt; 입니다. 기본 환경 세팅은 제외하고 추가나 필요한 코드 위주로 소개하겠습니다.&lt;/p&gt;
&lt;p&gt;OpenAPI Code Generator를 사용하기 위해 build.grardle.kts에 다음과 같이 플러그인 및 dependency 추가가 필요합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;plugin{
    id(&amp;quot;org.openapi.generator&amp;quot;) version &amp;quot;7.4.0&amp;quot;
    ...
}

dependency{
    implementation(&amp;quot;org.springframework.boot:spring-boot-starter-validation&amp;quot;)

    implementation(&amp;quot;com.squareup.okhttp3:okhttp:5.0.0-alpha.12&amp;quot;)
    implementation(&amp;quot;com.google.code.gson:gson:2.10.1&amp;quot;)
    ...
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;현재 Api 개발을 할 대상 컴포넌트를 &amp;#39;my-component&amp;#39;라고 가정합니다. 그리고 다른 컴포넌트들 api spec도 swagger hub에 명세되어 있다고 가정합니다. swagger hub에 my-component 프로젝트를 생성하여 Api spec을 작성합니다. swagger hub를 연동하기 위해 프로젝트 내부에 my-component와 타컴포넌트 api key들을 .swagger/hubapi.json에 추가해주었습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// hubapi.json
{
  &amp;quot;key&amp;quot;:&amp;quot;apikey 작성&amp;quot;,
  &amp;quot;test.componentA&amp;quot;: &amp;quot;apikey 작성&amp;quot; ,
  &amp;quot;test.componentB&amp;quot;: &amp;quot;apikey 작성&amp;quot; ,
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD9PpU/btsF2Djd85k/Ui4SCfKnfXOIxfvSWf80XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD9PpU/btsF2Djd85k/Ui4SCfKnfXOIxfvSWf80XK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD9PpU/btsF2Djd85k/Ui4SCfKnfXOIxfvSWf80XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD9PpU%2FbtsF2Djd85k%2FUi4SCfKnfXOIxfvSWf80XK%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;472&quot; height=&quot;88&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;OpenAPI를 통해 code를 생성하는 gradle task를 만듭니다. task에서 사용할 input에 대한 정의를 먼저 합니다. swagger hub에 작성한 api spec을 기준으로 Api 코드를 생성하고자 provide spec 에 작성합니다. 그리고 my-component 컴포넌트의 특정 api에서 타 컴포넌트에 있는 api를 호출하는 경우, 그 컴포넌트들의 정보를 consume spec에 작성합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;object UserDefineInputs {
    data class GenerateTaskInput(val name: String, val specUrl: String)

    val provideSpec = GenerateTaskInput(&amp;quot;my-component&amp;quot;, &amp;quot;https://api.swaggerhub.com/apis/test/my-component/1.0.0&amp;quot;)

    val consumeSpecs = listOf(
        GenerateTaskInput(&amp;quot;componentA&amp;quot;, &amp;quot;https://api.swaggerhub.com/apis/test/component-a/1.0.0&amp;quot;),
        GenerateTaskInput(&amp;quot;componentB&amp;quot;, &amp;quot;https://api.swaggerhub.com/apis/test/component-b/1.0.0&amp;quot;)
    )
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;현재 프로젝트에서 api spec 작성과 관리를 원격 환경인 &lt;strong&gt;swagger hub에서 하고 있어 그 기준으로 환경 설정이 됩니다.&lt;/strong&gt;&lt;/strong&gt; swagger hub는 유료이기에 open api generator 도입 전에 테스트하고자 하시면 로컬에서 간단히 openapi.yaml을 생성하여 open api generator를 사용할 수 있습니다. &lt;strong&gt;이럴 경우&lt;/strong&gt; &lt;strong&gt;swagger hub url이 아닌 yaml 파일 위치를 참조할 수 있도록 변경해주셔야 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;GenerateAllSpecs, GenerateProvideSpec, GenerateConsumeSpec task를 만들어 필요에 따라 provide, consume api 코드를 모두 생성/업데이트할 수 있도록 했습니다. 기본적으로 build 시 GenerateAllSpecs task와 동일하게 provide, consume spec 코드가 생성되도록 했습니다. 그리고 &lt;code&gt;src/&lt;/code&gt; 디렉토리에서 api 생성 코드들을 관리하여 버전 관리하고 싶다는 팀원의 니즈로 code generate 되는 위치를 지정합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;val generatedPath = &amp;quot;src/generated&amp;quot;
val generatedPathForKotlin = &amp;quot;$generatedPath/kotlin&amp;quot;

tasks.register(&amp;quot;GenerateAllSpecs&amp;quot;) {

    println(&amp;quot;&amp;gt;&amp;gt;&amp;gt; Deleting openapi generated files&amp;quot;)
    val genMetaFiles = files(&amp;quot;$generatedPath/.openapi-generator&amp;quot;)
    delete(genMetaFiles)

    val genSrcFiles = files(&amp;quot;./$generatedPath&amp;quot;)
    delete(genSrcFiles)

    println(&amp;quot;&amp;gt;&amp;gt;&amp;gt; Creating directory &amp;#39;src/generated&amp;#39; for openapi generated files&amp;quot;)
    val directory = file(&amp;quot;$generatedPathForKotlin/.generated_files&amp;quot;)
    Files.createParentDirs(directory)

    registeredGenerateTasks.forEach { it.taskProvider.get().doWork() }
}

tasks.register(&amp;quot;GenerateProvideSpec&amp;quot;) {
    registeredGenerateTasks.filter { it.isForProvide }.forEach { it.taskProvider.get().doWork() }
}

tasks.register(&amp;quot;GenerateConsumeSpec&amp;quot;) {
    registeredGenerateTasks.filter { !it.isForProvide }.forEach { it.taskProvider.get().doWork() }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;registerGenerateProvideSpec, registerGenerateConsumeSpec 에서 input에 정의한 api spec 문서를 참조하여 open api option에 따라 코드를 생성합니다. 또한 조금 더 빠른 코드 생성을 위해 각 컴포넌트별 gradle task가 만들어질 수 있도록 하였습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;data class RegisteredGenerateTask(
    val name: String,
    val isForProvide: Boolean,
    val taskProvider: TaskProvider&amp;lt;GenerateTask&amp;gt;
)

val registeredGenerateTasks = mutableListOf&amp;lt;RegisteredGenerateTask&amp;gt;()

fun registerGenerateTask(
    name: String,
    swaggerUrl: String,
    isForProvide: Boolean,
    configOptions: List&amp;lt;String&amp;gt;
): TaskProvider&amp;lt;GenerateTask&amp;gt; {
    val packageNamePrefix = if (isForProvide) &amp;quot;provide&amp;quot; else &amp;quot;consume&amp;quot;
    val apiPackageName = &amp;quot;${GenerateTaskConfig.PACKAGE_PREFIX}.$packageNamePrefix.test.$name.api&amp;quot;.replace(&amp;quot;[-]&amp;quot;.toRegex(), &amp;quot;_&amp;quot;)
    val modelPackageName = &amp;quot;${GenerateTaskConfig.PACKAGE_PREFIX}.$packageNamePrefix.test.$name.model&amp;quot;.replace(&amp;quot;[-]&amp;quot;.toRegex(), &amp;quot;_&amp;quot;)
    val taskName = &amp;quot;Generate${packageNamePrefix.capitalize()}_$name&amp;quot;
    val generatorName = if (isForProvide) &amp;quot;kotlin-spring&amp;quot; else &amp;quot;kotlin&amp;quot;

    val newTaskProvider = tasks.register&amp;lt;GenerateTask&amp;gt;(taskName) {
        generatorName.set(generatorName)
        outputDir.set(project.file(&amp;quot;./&amp;quot;).absolutePath)
        // 로컬에 있는 yaml 파일을 사용할 경우 다음 옵션 사용 inputSpec.set(yamlFilePath)
        remoteInputSpec.set(swaggerUrl)
        apiPackage.set(apiPackageName)
        modelPackage.set(modelPackageName)
        globalProperties.set(GenerateTaskConfig.globalOptions)
        configOptions.set(configOptions)
    }
    registeredGenerateTasks.add(RegisteredGenerateTask(name, isForProvide, newTaskProvider))
    return newTaskProvider
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;GenerateTaskConfig 에 open api code generate 옵션을 두어 관리하고 있습니다. open api code generate 옵션은 default 값이 있고 사용자가 옵션을 지정할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;object GenerateTaskConfig {
    private const val GENERATED_KOTLIN_SOURCE = &amp;quot;src/generated/kotlin&amp;quot;
    const val PACKAGE_PREFIX = &amp;quot;com.my.test&amp;quot;

    val globalOptions = mapOf(
        &amp;quot;apis&amp;quot; to &amp;quot;&amp;quot;,
        &amp;quot;models&amp;quot; to &amp;quot;&amp;quot;,
        &amp;quot;verbose&amp;quot; to &amp;quot;true&amp;quot;,
        &amp;quot;supportingFiles&amp;quot; to &amp;quot;&amp;quot;,
    )
    val serverConfigOptions = mapOf(
        &amp;quot;interfaceOnly&amp;quot; to &amp;quot;true&amp;quot;,
        &amp;quot;useSwaggerUI&amp;quot; to &amp;quot;true&amp;quot;,
        &amp;quot;useTags&amp;quot; to &amp;quot;true&amp;quot;,
        &amp;quot;enumPropertyNaming&amp;quot; to &amp;quot;UPPERCASE&amp;quot;,
        &amp;quot;sourceFolder&amp;quot; to GENERATED_KOTLIN_SOURCE
    )
    val clientConfigOptions = mapOf(
        &amp;quot;dateLibrary&amp;quot; to &amp;quot;java8&amp;quot;,
        &amp;quot;hideGenerationTimestamp&amp;quot; to &amp;quot;true&amp;quot;,
        &amp;quot;sourceFolder&amp;quot; to GENERATED_KOTLIN_SOURCE,
        &amp;quot;serializationLibrary&amp;quot; to &amp;quot;gson&amp;quot;,
        &amp;quot;library&amp;quot; to &amp;quot;jvm-okhttp4&amp;quot;
    )
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;consume한 컴포넌트들의 api를 사용하기 위해 api client를 이용하는 데 어려움을 겪어 clientConfigOptions를 설정하는데 어려움이 많았습니다.&lt;/p&gt;
&lt;p&gt;특정 팀원이 date가 다른 경우가 발생했었는데 OpenAPI Code generator의 dateLibrary 옵션 영향으로 발생했었던 거였습니다. 그래서 dateLibrary를 java8로 명시하여 해결하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://openapi-generator.tech/docs/generators/kotlin/&quot;&gt;kotlin-openapi-generator 공식문서&lt;/a&gt;를 통해 client 기본 library가 &lt;code&gt;jvm-okhttp4&lt;/code&gt; 인 것을 확인했습니다. &lt;code&gt;jvm-okhttp4&lt;/code&gt;라이브러리는 serialization 시 Moshi를 사용하고 있었습니다. OpenAPI Code generator는 다양한 설정을 통해 클라이언트 생성에 사용되는 라이브러리를 조정이 가능하여 dependency에 추가한 Gson을 이용하여 serialize 되도록 하였습니다. 즉, clientConfigOptions에 library와 serializationLibrary를 명시하여 해결하였습니다. 참고로 Java spring boot 일 경우 지원되는 library, serializationLibrary가 다르니 공식문서를 꼭 참조하시길 추천드립니다.&lt;/p&gt;
&lt;p&gt;설정 옵션에 따라 다음과 같이 client 에 있는 api를 호출할 때 필요한 infrastructure 코드들이 생성이 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wuLiZ/btsF1qLLYcd/9zwsxt5vPtMfW0tsPLI2Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wuLiZ/btsF1qLLYcd/9zwsxt5vPtMfW0tsPLI2Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wuLiZ/btsF1qLLYcd/9zwsxt5vPtMfW0tsPLI2Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwuLiZ%2FbtsF1qLLYcd%2F9zwsxt5vPtMfW0tsPLI2Uk%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;339&quot; height=&quot;491&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;앞서 정의한 registerGenerateTask 를 호출하여 spec 을 참조하여 code generate하는 task을 생성합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;UserDefineInputs.provideSpec.run { registerGenerateTask(name, specUrl, true, GenerateTaskConfig.serverConfigOptions) }
UserDefineInputs.consumeSpecs.forEach { spec -&amp;gt;
    spec.run {
        registerGenerateTask(name, specUrl, false, GenerateTaskConfig.clientConfigOptions)
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;로컬환경 IntelliJ IDEA에서 OpenAPI code generate task를 실행할 때 Gradle의 기본 Java 힙 메모리(512Mb)가 부족하여 클라이언트 생성에 실패할 수 있습니다. 원활하게 code generate를 하기 위해 Gradle 설정 파일인 gradle.properties를 수정하여 Java 힙 메모리 크기를 늘립니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;org.gradle.jvmargs=-Xmx8192m&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;적용&lt;/h3&gt;
&lt;h4&gt;1) Swagger hub에 명세한 API 문서에 따라 API 개발&lt;/h4&gt;
&lt;p&gt;앞서 Swagger hub에 my-component에 명세했었습니다. GenerateAllSpec task를 실행하게 되변 my-component, 즉 provide spec을 참조하여 MyApi가 생성됩니다. 이 때, Swagger hub에 명세한 response 클래스도 생성되어 API 클래스의 메서드 return 타입은 response가 됩니다. Controller 클래스에서 MyApi 클래스를 상속하여, MyApi에 명세된 메서드들을 override하여 개발하게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@RestController
class MyApiController(
    private val systemService: SystemService
    private val personService: PersonService
) : MyApi {
    private companion object : LoggerCreator()

    override fun systemHealthCheck(): ResponseEntity&amp;lt;DefaultResponse&amp;gt; = systemService.systemHealthCheck()

    override fun getAllPersons(): List&amp;lt;Person&amp;gt;? = personService.getAllPersons()

    ...
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;2) 타컴포넌트 API 호출&lt;/h4&gt;
&lt;p&gt;component A, component B 등 consume한 컴포넌트들의 client를 이용하기 위해, ApiConstants 의 companion object에 호출할 api를 작성합니다.companion object를 사용하면 해당 객체 내의 속성은 클래스 로딩 시점에 초기화되며, 이후에는 해당 클래스의 인스턴스나 다른 객체에서 접근하더라도 초기화 과정이 다시 일어나지 않습니다. 따라서 companion object 내에서 초기화된 api들은 클래스가 로드될 때 한 번만 호출되어 초기화됩니다. 이후에는 항상 같은 인스턴스를 반환하게 됩니다. 즉, 싱글톤으로 사용이 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;object ApiConstants : LoggerCreator() {
    init {
        log.info(&amp;quot;[INITIALIZED] api list for openapi consume project&amp;quot;)
    }
    val aaApi = ComponentATestAApi()  
    val abApi = ComponentATestBApi()
    val baApi = ComponentBTestAApi()
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;@Service
class serviceService(){
    fun hello(){
        println(&amp;quot;hello to componentA&amp;quot;)
        ApiConstants.aaApi.hello()
        println(&amp;quot;hello to componentB&amp;quot;)
        ApiConstants.baApi.hello()
    }

    fun bye(){
        println(&amp;quot;bye bye to componentA&amp;quot;)
        ApiConstants.abApi.bye()
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;참조&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://swagger.io/blog/api-development/integrating-with-the-swaggerhub-api/&quot;&gt;integrating-with-the-swaggerhub-api&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://openapi-generator.tech/docs/generators/kotlin/&quot;&gt;openapi-generator-kotlin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java/Spring</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/91</guid>
      <comments>https://yeondube.tistory.com/entry/kotlin-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-openapi-code-generator-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#entry91comment</comments>
      <pubDate>Sun, 24 Mar 2024 23:59:16 +0900</pubDate>
    </item>
    <item>
      <title>[querydsl] mysql filesort관련 group by 성능 최적화</title>
      <link>https://yeondube.tistory.com/entry/querydsl-mysql-filesort%EA%B4%80%EB%A0%A8-group-by-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;mysql 환경에서 querydsl 를 도입하면서 성능 저하 문제를 피하기 위해 고민하고 있습니다. 데이터 조회 시 사용하는 group by 쿼리에서 불필요한 &lt;code&gt;filesort&lt;/code&gt; 로 성능저하가 발생할 수 있습니다. &lt;code&gt;filesort&lt;/code&gt;는 MySQL 서버가 메모리 버퍼에 담을 수 없는 방대한 데이터를 정렬하기 위해 임시 파일을 사용하는 방식입니다. 이는 불필요한 디스크 I/O 작업을 증가시켜 쿼리 실행 속도를 크게 저하시킵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;nl-join-order-first-tbl.png&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkjHh0/btsF4x3uiJe/aNXPkK3Aupzzrfh6f6KFe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkjHh0/btsF4x3uiJe/aNXPkK3Aupzzrfh6f6KFe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkjHh0/btsF4x3uiJe/aNXPkK3Aupzzrfh6f6KFe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkjHh0%2FbtsF4x3uiJe%2FaNXPkK3Aupzzrfh6f6KFe1%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;360&quot; height=&quot;372&quot; data-filename=&quot;nl-join-order-first-tbl.png&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스트에서는 querydsl group by 쿼리에서 발생하는 filesort 문제를 분석하고, order_by_null 옵션을 활용하여 Filesort를 효과적으로 방지하는 방법을 소개합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MySQL 정렬 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL은 데이터 정렬을 위해 다음 두 가지 방식을 사용합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인덱스 정렬&lt;br /&gt;데이터가 이미 정렬된 상태로 저장된 인덱스를 활용하여 정렬 작업을 수행합니다. 인덱스 정렬은 메모리 접근만으로 이루어지기 때문에 Filesort에 비해 훨씬 빠른 속도를 제공합니다. MySQL Optimizer는 인덱스 정렬이 가능한 실행계회에 우선순위를 두고 선택합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 메모리 접근만으로 이루어지기 때문에 Filesort에 비해 훨씬 빠른 속도를 제공합니다. 쿼리 실행 계획에 &lt;code&gt;using index&lt;/code&gt;라는 메시지가 표시되어 정렬 방식을 쉽게 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;단점: 인덱스가 많아지면 InnoDB 버퍼 풀을 위한 메모리 사용량이 증가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Filesort
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GROUP BY 또는 ORDER BY 절에 인덱스를 사용할 수 없는 경우&lt;/li&gt;
&lt;li&gt;정렬 대상 데이터가 메모리 버퍼 크기를 초과하는 경우&lt;/li&gt;
&lt;li&gt;복잡한 쿼리 조건으로 인해 인덱스 사용이 비효율적인 경우&lt;/li&gt;
&lt;/ul&gt;
인덱스를 사용할 수 없는 경우에 MySQL은 데이터를 임시 파일에 저장하고 외부 정렬 알고리즘을 사용하여 정렬 작업을 수행합니다. Filesort는 다음과 같은 경우 발생합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 인덱스 정렬 방식에 비해 유연하게 사용할 수 있습니다. ORDER BY 절에 여러 컬럼을 지정하거나 복잡한 정렬 조건을 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;단점: 데이터량이 많거나 정렬 대상 데이터가 메모리 버퍼 크기를 초과하면 성능 저하가 발생합니다. 쿼리 실행 계획에 &lt;code&gt;using filesort&lt;/code&gt;라는 메시지가 표시되어 성능 문제 진단에 도움이 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Filesort 발생 원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Querydsl Group By 쿼리에서 Filesort가 발생하는 주요 원인을 정리하자면 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Group By 대상 컬럼에 인덱스가 없는 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL에서 group by 쿼리 실행 시 group by 대상 컬럼에 인덱스를 사용하여 데이터를 그룹화합니다. 하지만 group by 대상 컬럼에 인덱스가 없는 경우, MySQL은 데이터를 전체 테이블에서 스캔하여 그룹화해야 하며 이 과정에서 Filesort가 발생하게 됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ORDER BY 절에 Group By 대상 컬럼이 없는 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORDER BY 절에 group by 대상 컬럼이 없는 경우, MySQL은 먼저 group by 작업을 수행한 후 결과 데이터를 다시 정렬해야 합니다. 이때 Group By 결과 데이터가 메모리 버퍼 크기를 초과하면 filesort가 발생하게 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Filesort 발생 시 문제점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Filesort 발생 시 다음과 같은 문제점이 발생할 수 있습니다.&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;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Filesort는 다음과 같은 과정에서 불필요한 디스크 I/O 작업을 증가시켜 쿼리 실행 속도를 크게 저하시킵니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 정렬 대상 데이터를 메모리 버퍼에 담을 수 없는 경우, MySQL은 데이터를 임시 파일에 저장합니다.
2. 임시 파일에 저장된 데이터를 외부 정렬 알고리즘을 사용하여 정렬합니다.
3. 정렬된 데이터를 다시 메모리에 로드하여 쿼리 결과에 반영합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 여러 번 서비스 요청 등 많은 데이터 조회시 쿼리 실행 시간이 지연되어 사용자 경험에 악영향을 미칠 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서버 부하 증가&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Filesort는 CPU 및 메모리 자원을 많이 소모하여 서버 부하를 증가시킵니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU 사용량 증가: 외부 정렬 알고리즘은 CPU를 집중적으로 사용합니다.&lt;/li&gt;
&lt;li&gt;메모리 사용량 증가: 정렬 대상 데이터가 메모리 버퍼에 담을 수 없는 경우, 임시 파일을 저장하기 위한 추가 메모리가 필요합니다.&lt;/li&gt;
&lt;li&gt;디스크 I/O 사용량 증가: 임시 파일 읽고 쓰기 작업이 증가하여 디스크 I/O 사용량이 증가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 다른 쿼리 실행 속도에도 영향을 미쳐 전체 시스템 성능 저하를 초래할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Filesort 방지 방법&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인덱스 활용&lt;br /&gt;ORDER BY 절에 지정된 컬럼에 인덱스를 생성하면 MySQL은 인덱스를 사용하여 데이터를 정렬하므로 Filesort를 방지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;order_by_null 옵션 활용&lt;br /&gt;ORDER BY 절에 group by 대상 컬럼을 포함하고, 해당 컬럼 값이 null인 경우 맨 앞 또는 맨 뒤로 정렬하도록 지정하면 Filesort를 방지할 수 있습니다.&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;WHERE 절 조건 활용: 불필요한 데이터를 쿼리 결과에서 제외하여 정렬 대상 데이터의 양을 줄일 수 있습니다.&lt;/li&gt;
&lt;li&gt;SELECT 절 최적화: 필요한 컬럼만 SELECT하여 정렬 대상 데이터의 양을 줄일 수 있습니다.&lt;/li&gt;
&lt;li&gt;GROUP BY 절 최적화: group by 대상 컬럼을 최소화하여 group by 작업에 소요되는 시간을 줄일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;QueryDsl에서 order_by_null 옵션 활용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서 Querydsl group by 쿼리에서 filesort를 방지하기 위해 order_by_null 옵션을 사용하고 있습니다. 다음은 프로젝트에서 사용중인 코드를 예시로 만들었습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;fun searchAll(
        searchParam: SearchParam,
    ): List&amp;lt;ProductDetail&amp;gt; = queryFactory.selectFrom(productDetail)
        .where(
            buildConditions(searchParam)
        )
        .applyGroupBy(searchParam.groupByList)
        .orderBy(OrderByNull.DEFAULT).fetch()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;searchParam에 있는 groupByList에 있는 groupBy 대상 컬럼들이 인덱스에 없다면? querydsl에서 group by 대상 기준으로 using filesort옵션이 활성화되어 정렬을 하게 됩니다. 아직 querydsl에서는 using filesort을 조작할 수 있는 유틸함수를 제공해주지 않는 것 같았습니다. 별도로OrderByNull 클래스를 만들어 group by 시 사용하고 있습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;/**
 * group by 시 filesort 방지
 */
class OrderByNull private constructor() : OrderSpecifier&amp;lt;Comparable&amp;lt;*&amp;gt;&amp;gt;(
    Order.ASC, Expressions.nullExpression(), NullHandling.Default
) {
    companion object {
        val DEFAULT: OrderByNull = OrderByNull()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Querydsl group by 쿼리에서 filesort는 쿼리 실행 속도를 크게 저하시키고 서버 부하를 증가시키는 문제점을 야기합니다. 따라서 인덱스 활용, order_by_null 옵션 활용, 쿼리 최적화 등의 방법을 통해 filesort를 방지하고 쿼리 성능을 향상시키는 것이 중요합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고자료&lt;/h2&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://jojoldu.tistory.com/477&quot;&gt;https://jojoldu.tistory.com/477&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java/Spring</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/90</guid>
      <comments>https://yeondube.tistory.com/entry/querydsl-mysql-filesort%EA%B4%80%EB%A0%A8-group-by-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94#entry90comment</comments>
      <pubDate>Sun, 17 Mar 2024 23:59:08 +0900</pubDate>
    </item>
    <item>
      <title>[kotlin] 동시성 제어하기</title>
      <link>https://yeondube.tistory.com/entry/kotlin-%EB%8F%99%EC%8B%9C%EC%84%B1</link>
      <description>&lt;p&gt;동시성 문제는 여러 개의 스레드가 동시에 실행될 때 발생할 수 있는 문제로, 데이터 일관성 문제, 경쟁 조건 등이 대표적인 예입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.jpg&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;237&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OCzsI/btsFF00rM1j/7rk9ZIhpcttPF9ipRzFZOK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OCzsI/btsFF00rM1j/7rk9ZIhpcttPF9ipRzFZOK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OCzsI/btsFF00rM1j/7rk9ZIhpcttPF9ipRzFZOK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOCzsI%2FbtsFF00rM1j%2F7rk9ZIhpcttPF9ipRzFZOK%2Fimg.jpg&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;420&quot; height=&quot;237&quot; data-filename=&quot;img.jpg&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;237&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터 일관성 문제: 여러 스레드가 동일한 데이터를 동시에 수정할 때, 데이터의 일관성이 깨질 수 있습니다. 예를 들어, 한 스레드가 데이터를 수정하는 도중에 다른 스레드가 데이터를 수정하면 데이터가 손상될 수 있습니다.&lt;/li&gt;
&lt;li&gt;경쟁 조건: 여러 스레드가 서로 경쟁하면서 자원을 사용할 때, 스레드 간의 충돌로 인해 예상치 못한 결과가 발생할 수 있습니다. 예를 들어, 한 스레드가 자원을 사용하고 있는 도중에 다른 스레드가 자원을 사용하려고 하면 충돌이 발생할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이번 포스트에서는 &lt;strong&gt;포인트 선물하기&lt;/strong&gt;를 코틀린에서 동시성 문제를 해결할 때 사용할 수 있는 방법들을 소개하고자 합니다 .&lt;/p&gt;
&lt;h3&gt;volatile&lt;/h3&gt;
&lt;p&gt;공유 변수의 값이 변경될 때마다 모든 스레드가 해당 값을 최신 상태로 읽을 수 있도록 하는 키워드입니다. 변수 값 변경 시 모든 스레드에 즉시 알립니다. &lt;a href=&quot;https://kotlinlang.org/docs/whatsnew19.html&quot;&gt;Kotlin 1.9.0&lt;/a&gt; 에서 kotlin.concurrent.Volatile 은 stable로 지원합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Account(var balance: Int = 0) {
    companion object {
        val totalBalance = AtomicInteger(0)
    }
}

fun sendPoint(sender: Account, receiver: Account, amount: Int) = runBlocking {
    sender.balance -= amount
    receiver.balance += amount
    Account.totalBalance.addAndGet(amount)
    println(&amp;quot;[SEND] $amount&amp;quot;)
}

fun main() {
    val sender = Account(100)
    val receiver = Account(0)
    val jobs = List(5) {
        launch {
            sendPoint(sender, receiver, 10)
        }
    }
    jobs.forEach { it.join() }
    println(&amp;quot;[TOTAL] ${Account.totalBalance}&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;하지만 @volatile은 atomic read, write는 보장하지만 atomicity of larger actions을 보장하지 않습니다. 즉, 변수 값 읽기/쓰기만 동기화합니다. 여러 스레드가 동시에 변수를 증가시키는 경우, 읽기/쓰기 사이에 다른 스레드의 변경 내용이 반영되지 않을 수 있습니다.&lt;/p&gt;
&lt;p&gt;totalBalance 변수를 증가시키는 task이 여러 단계로 이루어져 있다면, 이 작업 전체가 원자적으로 수행되지 못할 수 있습니다. 만약 두 스레드가 동시에 point를 보내는 작업을 수행한다면, 다음과 같은 상황이 발생할 수 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;첫 번째 스레드가 totalBalance 변수의 값을 읽습니다.&lt;/li&gt;
&lt;li&gt;두 번째 스레드가 totalBalance 변수의 값을 읽습니다.&lt;/li&gt;
&lt;li&gt;첫 번째 스레드가 읽은 값에 1을 더하고, 더한 값을 totalBalance 변수에 씁니다.&lt;/li&gt;
&lt;li&gt;두 번째 스레드가 읽은 값에 1을 더합니다.&lt;/li&gt;
&lt;li&gt;두 번째 스레드가 더한 값을 totalBalance 변수에 씁니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;fun sendPoint(sender: Account, receiver: Account, amount: Int) = runBlocking {
    sender.balance -= amount
    receiver.balance += amount

    // totalBalance 수정 (비원자적)
    val prevTotalBalance = Account.totalBalance.get()
    val newTotalBalance = prevTotalBalance + amount
    Account.totalBalance.compareAndSet(prevTotalBalance, newTotalBalance)

    println(&amp;quot;[SEND] $amount&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;@Volatile은 공유자원에 접근하여 write하는 스레드가 1개이고 공유자원을 read하는 스레드가 여러 개일 경우 유용합니다. 하지만 여러 스레드에서 동시에 접근하여 read/write 하는 공유자원은 @Volatile만으로는 안전하지 않습니다. 자원의 가시성을 확보하여 동기화에 유용하지만, 원자성 연산을 보장하지 않아 동시성 이슈를 해결에 충분하지 못합니다.&lt;/p&gt;
&lt;h3&gt;synchronized&lt;/h3&gt;
&lt;p&gt;한 번에 한 스레드만 접근할 수 있도록 하는 방법입니다. synchronized method or block을 스레드가 참조하는 동안 다른 스레드는 대기하게 됩니다.&lt;/p&gt;
&lt;h4&gt;1) synchronized method&lt;/h4&gt;
&lt;p&gt;메서드 전체가 동기화되어야 하거나 간단하고 직관적인 동기화 방법입니다. 하지만 상대적으로 성능 저하 가능성이 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;class Account(var balance: Int = 0) {
  companion object {
    @Volatile
    var totalBalance = 0

    @Synchronized
    fun sendPoint(sender: Account, receiver: Account, amount: Int) {
      // 송금 전에 송금자가 충분한 잔고를 가지고 있는지 확인
      if (sender.balance &amp;gt;= amount) {
        sender.balance -= amount
        receiver.balance += amount
        totalBalance += amount
        println(&amp;quot;[SEND] $amount 포인트&amp;quot;)
        receiver.receivePoint(amount)
      } else {
        println(&amp;quot;[FAILED] 송금자가 잔고 부족&amp;quot;)
      }
    }
  }

  @Synchronized
  fun receivePoint(amount: Int) {
    balance += amount
    println(&amp;quot;[RECEIVE] $amount 포인트를 받았습니다.&amp;quot;)
  }
}

fun main() {
  val sender = Account(100)
  val receiver = Account(0)
  val jobs = List(5) {
    launch {
      Account.sendPoint(sender, receiver, 10)
    }
  }
  jobs.forEach { it.join() }
  println(&amp;quot;[TOTAL] ${Account.totalBalance}&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2) synchronized block&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;class Account(var balance: Int = 0) {
  companion object {
    @Volatile
    var totalBalance = 0

    fun sendPoint(sender: Account, receiver: Account, amount: Int) {
       // 송금 전에 송금자가 충분한 잔고를 가지고 있는지 확인
        if (sender.balance &amp;lt; amount) {
          println(&amp;quot;[FAILED] 송금자가 잔고 부족&amp;quot;)
        }
        synchronized(this) {
          sender.balance -= amount
          receiver.balance += amount
          totalBalance += amount
          println(&amp;quot;[SEND] $amount 포인트&amp;quot;)
          receiver.receivePoint(amount)
        }
      }
    }
  }

  fun receivePoint(amount: Int) {
    synchronized(this) {
      balance += amount
      println(&amp;quot;[RECEIVE] $amount 포인트를 받았습니다.&amp;quot;)
    }
  }
}

fun main() {
  val sender = Account(100)
  val receiver = Account(0)
  val jobs = List(5) {
    launch {
      Account.sendPoint(sender, receiver, 10)
    }
  }
  jobs.forEach { it.join() }
  println(&amp;quot;[TOTAL] ${Account.totalBalance}&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;synchronized는 임계 구역을 보호하는 데 유용합니다. 하지만 위 코드에서는 다음과 같은 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;synchronized 사용으로 인해 전체 Account 객체에 대한 lock이 걸려 성능 저하&lt;/li&gt;
&lt;li&gt;totalBalance 업데이트 시에도 전체 객체 lock 필요 -&amp;gt; 불필요한 락으로 인한 동시성 제어 효율 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;임계 구역 내에서 다른 스레드의 작업 완료를 기다리는 경우, 현재 스레드가 자신을 블록하여 CPU 자원을 낭비합니다. 그리고 다른 스레드가 임계 구역을 떠날 때까지 Unlock을 기다리는 스레드들이 CPU 자원을 소모하며 대기하게 됩니다. 스레드 경쟁이 심할수록 더 많은 스레드들이 블록 및 잠금 해제 대기를 하게 됩니다. 임계 구역 코드가 길다면 낭비 가능성이 더욱 높아지게 됩니다.&lt;/p&gt;
&lt;p&gt;synchronized 키워드 방식은 병렬성에 취약하여 대규모 서비스에서 synchronized 키워드를 많이 사용할 경우 심각한 성능 저하가 생길 수 있습니다.&lt;/p&gt;
&lt;h3&gt;atomic 변수&lt;/h3&gt;
&lt;p&gt;원자적으로 연산을 수행할 수 있는 thread-safe 자료구조입니다. 여러 스레드가 동시에 접근하더라도 데이터 일관성을 유지할 수 있습니다. atomic 변수는 java.util.concurrent 패키지에 있으며, AtomicInteger, AtomicLong 등이 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Account(var balance: Int = 0) {
    companion object {
        val totalBalance = AtomicInteger(0)
    }
}

fun sendPoint(sender: Account, receiver: Account, amount: Int) = runBlocking {
    sender.balance -= amount
    receiver.balance += amount
    Account.totalBalance.addAndGet(amount)
    println(&amp;quot;[SEND] $amount points&amp;quot;)
}

fun main() {
    val sender = Account(100)
    val receiver = Account(0)
    val jobs = List(5) {
        launch {
            sendPoint(sender, receiver, 10)
        }
    }
    jobs.forEach { it.join() }
    println(&amp;quot;[TOTAL] ${Account.totalBalance}&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;atomic 변수는 다음과 같은 장점을 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;atomic 변수는 compare-and-swap(CAS) 연산을 사용하여 원자적으로 값을 변경합니다. CAS 연산을 통해 다른 스레드의 접근을 막지 않기 때문에 여러 스레드가 동시에 접근하더라도 데이터 일관성을 유지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;atomic 변수는 일반적인 변수와 달리 메모리에 한 번만 저장됩니다. 이는 여러 스레드가 동시에 접근할 때 메모리 접근 횟수를 줄일 수 있어 메모리 효율성을 높일 수 있습니다.&lt;/li&gt;
&lt;li&gt;atomic 변수를 사용하면 여러 스레드가 동시에 접근할 때, context switching이 감소합니다. atomic 변수가 원자적으로 연산을 수행하기 때문에 스레드 간의 충돌을 방지하고,lock 사용을 줄임으로써 context switching이 최소화하여 시스템의 성능을 향상 시킬 수 있습니다.&lt;br&gt;하지만 복잡한 경우에는 다른 방식을 사용해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;thread-safe (concurrent) collections&lt;/h3&gt;
&lt;p&gt;여러 스레드가 동시에 접근할 수 있는 컬렉션입니다. 스레드 안전성을 보장하며, 동시성 작업을 효율적으로 수행할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Account(private val initialBalance: Int) {
  private val balance = AtomicInteger(initialBalance)

  fun sendPoint(amount: Int, receiver: Account) {
    if (balance.addAndGet(-amount) &amp;lt; 0) {
      println(&amp;quot;[FAILED] ${this.name}님의 포인트가 부족합니다.&amp;quot;)
      return
    }

    receiver.receivePoint(amount)
    println(&amp;quot;[SEND] ${this.name}님이 ${receiver.name}님에게 ${amount}포인트를 선물했습니다.&amp;quot;)
  }

  fun receivePoint(amount: Int) {
    balance.addAndGet(amount)
    println(&amp;quot;[RECEIVE] ${this.name}님이 ${amount}포인트를 받았습니다.&amp;quot;)
  }
}

fun main() = runBlocking {
  // 캐시처럼 사용할 ConcurrentHashMap
  val users = ConcurrentHashMap&amp;lt;String, Account&amp;gt;()

  // 사용자 추가
  users[&amp;quot;Karina&amp;quot;] = Account(&amp;quot;Karina&amp;quot;, 100)
  users[&amp;quot;Giselle&amp;quot;] = Account(&amp;quot;Giselle&amp;quot;, 150)
  users[&amp;quot;Ningning&amp;quot;] = Account(&amp;quot;Ningning&amp;quot;, 50)
  users[&amp;quot;Winter&amp;quot;] = Account(&amp;quot;Winter&amp;quot;, 200)

  // 10개의 무작위 송금 작업 실행
  val jobs = List(10) {
    launch {
      val senderName = users.keys.random()
      val receiverName = users.keys.random()
      val amount = (1..10).random()

      // ConcurrentHashMap에 없는 사용자 확인
      val sender = users[senderName] ?: run {
        println(&amp;quot;[FAILED] ${senderName}님은 존재하지 않는 사용자입니다.&amp;quot;)
        return@launch
      }

      // 송금자 잔고 확인
      if (sender.balance.addAndGet(-amount) &amp;lt; 0) {
        println(&amp;quot;[FAILED] ${senderName}님의 포인트가 부족합니다.&amp;quot;)
        return@launch
      }

      // 수신자 정보 확인
      val receiver = users[receiverName] ?: run {
        println(&amp;quot;[FAILED] ${receiverName}님은 존재하지 않는 사용자입니다.&amp;quot;)
        return@launch
      }

      // 포인트 송금
      receiver.receivePoint(amount)
      println(&amp;quot;[SEND] ${senderName}님이 ${receiverName}님에게 ${amount}포인트를 선물했습니다.&amp;quot;)
    }
  }

  // 모든 작업 종료 기다림
  jobs.forEach { it.join() }

  // 최종 잔고 출력
  users.forEach { (name, account) -&amp;gt; println(&amp;quot;[TOTAL] $name님: ${account.balance.get()}포인트&amp;quot;) }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ConcurrentHashMap은 여러 사용자가 동시에 데이터에 접근해야 하는 경우 HashMap보다 ConcurrentHashMap을 사용하면 락을 사용하여 동시 접근을 제어하여 시스템의 안정적인 동작을 기대할 수 있습니다.&lt;br&gt;concurrent collection은 여러 사용자가 동시에 데이터를 읽고 쓰는 경우에 유용합니다. 그리고 여러 사용자가 동시에 티켓을 예약하는 경우 데이터 경쟁 가능성이 높은 경우에 concurrent collection을 활용합니다. 대표적으로 caching system이 있습니다. 다음은 ConcurrentHashMap을 사용하는 라이브러리입니다. &lt;a href=&quot;https://github.com/google/guava/wiki/CachesExplained&quot;&gt;explain Guava for cache&lt;/a&gt;, &lt;a href=&quot;https://github.com/ben-manes/caffeine&quot;&gt;explain Caffeine for cache&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;semaphore&lt;/h3&gt;
&lt;p&gt;lock 이 아닌 semaphore 기반으로 여러 스레드가 공유 자원에 접근하는 것을 제어합니다. 한 번에 접근할 수 있는 스레드의 수를 제한함으로써 데이터 일관성 문제를 예방할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Account(var balance: Int = 0)

fun sendPoint(sender: Account, receiver: Account, amount: Int, semaphore: Semaphore) = runBlocking {
  // 세마포어 획득
  semaphore.acquire()
  try {
    // 송금 전에 송금자가 충분한 잔고를 가지고 있는지 확인
    if (sender.balance &amp;gt;= amount) {
      sender.balance -= amount
      receiver.balance += amount
      println(&amp;quot;[SEND] $amount 포인트&amp;quot;)
    } else {
      println(&amp;quot;[FAILED] 송금자가 잔고 부족&amp;quot;)
    }
  } finally {
    // 세마포어 해제
    semaphore.release()
  }
}

fun main() = runBlocking {
  val sender = Account(100)
  val receiver = Account(0)
  val semaphore = Semaphore(1) // 이체 시 동시성을 1로 제한
  val jobs = List(5) {
    launch {
      sendPoint(sender, receiver, 10, semaphore)
    }
  }
  jobs.forEach { it.join() }
  println(&amp;quot;[TOTAL] sender : ${sender.balance}&amp;quot;)
  println(&amp;quot;[TOTAL] receiver: ${receiver.balance}&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;mutex&lt;/h3&gt;
&lt;p&gt;lock을 이용하여 한 번에 한 스레드만 접근할 수 있도록 합니다. 이를 통해 데이터 일관성 문제를 해결할 수 있습니다. 뮤텍스를 획득하고 해제할 경우 명시적으로 lock 획득/해제 호출이 필요합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Account(var balance: Int = 0)

fun sendPoint(sender: Account, receiver: Account, amount: Int, mutex: Mutex) = runBlocking {
    with(mutex) {
        // 잠금 획득
        lock()
        try {
            sender.balance -= amount
            receiver.balance += amount
            println(&amp;quot;[SEND] $amount points&amp;quot;)
        } finally {
            // 잠금 해제
            unlock()
        }
    }
}

fun main() = runBlocking {
    val sender = Account(100)
    val receiver = Account(0)
    val mutex = Mutex()
    val jobs = List(5) {
        launch {
            sendPoint(sender, receiver, 10, mutex)
        }
    }
    jobs.forEach { it.join() }
    println(&amp;quot;[TOTAL] sender : ${sender.balance}&amp;quot;)
    println(&amp;quot;[TOTAL] receiver: ${receiver.balance}&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;code&gt;withlock()&lt;/code&gt; with ReentrantLock&lt;/h3&gt;
&lt;p&gt;lock을 이용하여 한 번에 한 스레드만 접근할 수 있도록 합니다. block 내 코드를 실행하기 때문에 자동으로 lock 획득/해제가 됩니다. 이에 따라 블럭 withlock() 외부에 있는 코드는 lock 해제까지 영향을 받게 됩니다. ReentrantLock을 사용해서 다시 재진입이 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fun sendPoint(sender: Account, receiver: Account, amount: Int, lock: ReentrantLock) = runBlocking {
  lock.withLock {
    // 송금 전에 송금자가 충분한 잔고를 가지고 있는지 확인
    if (sender.balance &amp;gt;= amount) {
      sender.balance -= amount
      receiver.balance += amount
      println(&amp;quot;[SEND] $amount 포인트&amp;quot;)
    } else {
      println(&amp;quot;[FAILED] 송금자 잔고 부족&amp;quot;)
    }
  }
}

fun main() = runBlocking {
  val sender = Account(100)
  val receiver = Account(0)
  val lock = ReentrantLock()
  val jobs = List(5) {
    launch {
      sendPoint(sender, receiver, 10, lock)
    }
  }
  jobs.forEach { it.join() }
  println(&amp;quot;[TOTAL] sender : ${sender.balance}&amp;quot;)
  println(&amp;quot;[TOTAL] receiver: ${receiver.balance}&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;actors&lt;/h3&gt;
&lt;p&gt;Actor는 독립적인 실행 단위로, 자신의 상태와 메시지를 처리하는 로직을 가지고 있습니다. Actor 간의 통신은 메시지를 통해 이루어지며, 메시지는 Actor의 mailbox에 저장됩니다.&lt;/p&gt;
&lt;p&gt;스레드와는 달리, Actor는 상태를 가지지 않으며, 실행 중에 다른 Actor나 함수를 호출할 수 있습니다. 이로 인해 Actor 방식은 스레드 간의 경쟁 조건을 방지하고, 자원 관리를 간편하게 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sealed class Message {
    data class SendPoint(val amount: Int) : Message()
}

class Account(var balance: Int = 0)

class AccountActor(val account: Account) : CoroutineScope {
    private val channel = Channel&amp;lt;Message&amp;gt;()
    override val coroutineContext = SupervisorJob() + Dispatchers.Default + channel

    fun send(message: Message) {
        launch {
            channel.send(message)
        }
    }

    fun receive(): Message = channel.receive()
}

fun main() = runBlocking {
    val sender = Account(100)
    val receiver = Account(0)
    val senderActor = AccountActor(sender)
    val receiverActor = AccountActor(receiver)

    val jobs = List(5) {
        launch {
            senderActor.send(Message.SendPoint(10))
            receiverActor.send(Message.SendPoint(-10))
        }
    }

    jobs.forEach { it.join() }

    println(&amp;quot;[TOTAL] sender : ${sender.balance}&amp;quot;)
    println(&amp;quot;[TOTAL] receiver: ${receiver.balance}&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Message 클래스를 sealed 클래스로 정의하고, SendPoint 클래스를 그 하위 클래스로 정의합니다. sealed class를 사용하면 메시지의 타입이 명확하게 정의되기 때문에, 다른 스레드나 코루틴에서 잘못된 메시지를 전송하는 것을 방지할 수 있습니다. 이를 통해 안전한 동시성을 보장할 수 있습니다. 따라서 Message 클래스의 하위 클래스인 SendPoint, ReceivePoint 등을 정의하여 각각의 메시지 타입을 명확하게 구분할 수 있습니다.&lt;/p&gt;
&lt;p&gt;AccountActor 클래스를 정의하여 Account 객체와 통신할 수 있는 채널을 생성합니다. sendPoint() 메소드는 채널을 통해 amount 값을 전송하고, receivePoint() 메소드는 채널로부터 값을 수신합니다. main 함수에서는 AccountActor 객체를 생성하고, sendPoint() 메소드를 호출하여 sender와 receiver의 balance를 수정합니다. 모든 작업이 완료된 후에는 balance 값을 출력합니다.&lt;/p&gt;
&lt;h3&gt;결론&lt;/h3&gt;
&lt;p&gt;다음 상황에서 동기화 방식을 추천합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;일반적으로 synchronized나 ReentrantLock을 추천합니다.&lt;/li&gt;
&lt;li&gt;특정 변수값 변경 시 모든 스레드에 동기화가 필요한 간단한 경우에는 volatile, atomic 변수을 사용하는 것을 추천합니다. 예를 들면 10번 출석체크를 했는지 횟수를 카운팅하는 시스템에서 count 변수 증가를 할 때 적절합니다.&lt;/li&gt;
&lt;li&gt;여러 스레드가 동시에 컬렉션에 접근해야 하는 경우 concurrent 컬렉션을 이용하여 동기화를 추천합니다. 예를 들면 온라인 쇼핑몰에서 여러 고객이 동시에 제품 목록을 조회하고 장바구니에 담는 시스템에서 productList 컬렉션 관리할 때 적절합니다.&lt;/li&gt;
&lt;li&gt;특정 메서드 실행 중 다른 스레드의 접근을 차단해야 하는 경우에는 synchronized method 동기화를 추천합니다. 예시로는 파일 시스템에서 파일을 읽고 쓰는 메서드 동기화가 있습니다.&lt;/li&gt;
&lt;li&gt;특정 코드 블록만 동기화해야 하는 경우에는 synchronized block 동기화를 추천합니다. 예시로 여러 스레드가 동시에 데이터베이스에 접근해야 하는 경우, 접근 코드만 동기화가 있습니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Java/Spring</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/89</guid>
      <comments>https://yeondube.tistory.com/entry/kotlin-%EB%8F%99%EC%8B%9C%EC%84%B1#entry89comment</comments>
      <pubDate>Sun, 10 Mar 2024 23:59:14 +0900</pubDate>
    </item>
    <item>
      <title>Java 기본 개념</title>
      <link>https://yeondube.tistory.com/entry/Java-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Java 자료형&lt;/span&gt;&lt;/h2&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;Primitive&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;실제 데이터 값을 저장하는 자료형&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;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;boolean, char, byte(-128-127) , short, int, long, float, double&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;Reference Type&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;메모리 주소 값을 통해 객체를 참조하는 타입&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;실제 객체&lt;/b&gt; - &lt;b&gt;힙&lt;/b&gt; 영역에 저장&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;참조 타입 변수&lt;/b&gt;&amp;nbsp;-&amp;nbsp;&lt;b&gt;스택&lt;/b&gt;&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;/ul&gt;
&lt;pre id=&quot;code_1627581072727&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Integer a = (Integer) 500;
Integer b = (Integer) 500;
System.out.println(a==b); //false

Integer a = 127;
Integer b = 127;
System.out.println(a==b); //true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;IntegerCache.low&amp;nbsp;보다 크고&amp;nbsp;IntegerCache.high&amp;nbsp;보다 작은 int 리터럴&amp;nbsp;i를 넘긴다면,&amp;nbsp;&lt;b&gt;Integer.valueOf()&lt;/b&gt;&amp;nbsp;메소드는&amp;nbsp;IntegerCache&amp;nbsp;로부터 Integer 객체들을 리턴 &lt;b&gt;IntegerCache.low&lt;/b&gt;&amp;nbsp;와&amp;nbsp;&lt;b&gt;IntegerCache.high&lt;/b&gt;&amp;nbsp;의 기본값은 각각&amp;nbsp;-128과&amp;nbsp;127.&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;b&gt;Integer.valueOf()&lt;/b&gt;&amp;nbsp;메소드는&amp;nbsp;&lt;b&gt;-128&lt;/b&gt;&amp;nbsp;에서&amp;nbsp;&lt;b&gt;127&lt;/b&gt;&amp;nbsp;사이의 int literal을 넘겨줄 때, 새로운 Integer 객체를 생성하고 리턴하는 대신, 내부의&amp;nbsp;&lt;b&gt;IntegerCache&lt;/b&gt; 객체에서 Integer 객체를 반환한다. 캐시는 static block에 의해 IntegerCache 클래스가 메모리에 로드되는 처음에 초기화된다. Java는 -128 부터 127 범위의 정수 객체를 캐싱한다. 이 범위의 정수들은 개발할 때 빈번하게 사용되기 때문이다. Integer 캐시는 범위를 수정할 수 있지만 그외 타입 캐시는 수정 불가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Integer과 int 크기 비교&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;- int : Primitive type&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;- Integer : Wrapper 클래스&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;약 5배차이&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Object 8 byte&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Integer 16 byte&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Integer 참조하는 변수 4 byte&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Integer 사이즈 = 16+4 byte&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;&lt;span style=&quot;color: #000000; font-size: 1.62em; letter-spacing: -1px; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Primitive type과 Wrapper class&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Primitive type : int&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;산술연산 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;null로 초기화 불가능&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;Wrapper class&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;rarr; Unboxing으로 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;null 값 처리 가능 &amp;rarr; DB에서 특정 컬럼의 자료형이 정수형이지만 null이 들어올 수 있을 때 VO에서 Integer를 사용하여 null인 경우를 처리할 수 있다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Boxing과 Unboxing&lt;/span&gt;&lt;/h2&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;Boxing&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;Primitive 자료형 &amp;rarr; Wrapper 클래스&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;Unboxing&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;Wrapper 클래스 &amp;rarr; Primitive&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;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;/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;pre id=&quot;code_1627581232634&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Star{

	private static Star instance = new Star();

	private Star() {}

  	public static Star getInstance() {
      		return instance;
  	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://webdevtechblog.com/%EC%8B%B1%EA%B8%80%ED%84%B4-%ED%8C%A8%ED%84%B4-singleton-pattern-db75ed29c36&quot;&gt;싱글톤 패턴 작성 방식&lt;/a&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;eager intialization&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;위 코드의 방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스가 최초로 로딩 될 때 객체가 생성되기때문에 Thread-safe&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;Lazy Initialization with synchronized&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;동적 바인딩(dynamic binding)(런타임시에 성격이 결정됨)을 통해 인스턴스를 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;synchronized 키워드를 이용한 게으른 초기화 방식인데, 메서드에 동기화 블럭을 지정해서 Thread-safe&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;Lazy Initialization. Double Checking Locking(DCL, Thread-safe)&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;인스턴스가 생성되지 않은 경우에만 동기화 블럭이 실행되게끔 구현하는 방식&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;Lazy Initialization. LazyHolder(게으른 홀더, Thread-safe)&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;volatile 이나 synchronized 키워드 없이도 동시성 문제를 해결&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바와 스프링의 싱글톤 차이점&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;b&gt;싱글톤 객체의 생명주기가 다르다&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바 싱글톤 객체의 범위는 &lt;b&gt;클래스 로더&lt;/b&gt;가 기준&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;어플리케이션 컨텍스트&lt;/b&gt;가 기준&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Bean을 등록할 때 범위의 default는 Singleton다. 싱글톤 이외 범위는 prototype, request, session이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;final&lt;/span&gt;&lt;/h2&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;클래스 앞에 final키워드가 붙으면 '자식을 가질수 없는 최종 클래스'&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드 앞에 final키워드가 붙으면 '더이상 오버라이딩 할 수 없는 최종 메소드'&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;변수 앞에 final은 상수화&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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 style=&quot;color: #ee2323;&quot;&gt;주의!! &lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;final int[] array로 할당된 그 변수의 수정 (혹은 새로운 값 할당)이 금지된거지, 그 변수가 가리키고 있는 참조안에 있는 값들이 immutable이라는 뜻은 아님&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;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;/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;매개변수가 같아야 한다.&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;접근 제어자&lt;/b&gt;를 부모 클래스의 메서드보다 좁은 범위로 변경할 수 없다.&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;예외&lt;/b&gt;는 부모 클래스의 메서드보다 많이 선언할 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인스턴스 메서드를 static메서드로 또는 그 반대로 변경할 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상속 키워드&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인터페이스 &amp;lt;- 클래스 implements &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인터페이스 &amp;lt;- 인터페이스 extends★&lt;/span&gt;&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;추상클래스&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단일상속&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;is-a&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인터페이스&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다중상속&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;has-a&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;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인터페이스 멤버변수는 &lt;b&gt;public static final&lt;/b&gt; 고정&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;public&lt;/b&gt; 고정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상속받은 메서드는 overriding하더라도 public&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;자바 8 default 메서드 쓸 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바 9 private, private static를 쓸 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;public - protected - default - private 전체 - (다른패키지)자손/상속 - 같은패키지 - 같은클래스&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주의!!! 최상위 클래스에만 사용될 수 있는 접근제한자는 &lt;b&gt;public&lt;/b&gt;이다&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;ol 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;참조변수가 더 상위클래스 &amp;gt;=&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;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;참조변수의 클래스에 해당 메서드가 있는가&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;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;new로 생성되는 (오른쪽) 클래스에 해당 메서드가 있는가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;new로 생성되는 (오른쪽) 클래스까지 하위 클래스 중에 해당 메서드가 있는가&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;instanceof 연산자&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;null instanceof Object&lt;/span&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;instanceof연산에서 앞에 null이 오면 무조건 false를 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Collection 인터페이스&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;List 인터페이스&lt;/span&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;ArrayList&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;synchronized를 이용한 과도한 동기화는 성능을 떨어뜨릴 수 있고 교착 상태에 빠뜨릴 수 있으며, 예측할 수 없는 동작을 유발&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;CopyOnWriteArrayList&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;내부의 배열은 절대 변경할 수 없으므로 순회할 때 락이 필요 없어서 속도면에서 매우 빠르다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;List를 읽기 위해 어딘가에 전달할 때 원본이 아닌 복사본을 만들어서 전달&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;synchronizedList&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;한번에 하나의 스레드만 접근&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;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Set 인터페이스&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;Set에 null이 추가된다&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;Queue 인터페이스&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Map 인터페이스&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;&lt;u&gt;&lt;b&gt;Map은 컬렉션을 상속하지 않는다&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HashMap 키에 null이 된다&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;HashMap의 키에 오는 클래스에 hashcode + equals 재정의&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컬렉션에서 오토박싱/언박싱&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오토 언박싱&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;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오토 박싱&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;컬렉션에는 무적권 &quot;객체&quot;만 저장가능&lt;/b&gt;!&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Comparable Comparator 인터페이스&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;메소드 1개밖에 없음&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1627565577729&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; new Priority Queue((a, b) -&amp;gt; { return a-b;});&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;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;FileInputStream 비문자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;FileReader 문자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;BufferedReader로 개행단위&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1627581257074&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;File file = new File(&quot;c:\\example\\File\\umejintan_new.txt&quot;);
input = new FileInputStream(file);
int readBuffer = 0;
byte [] buffer = new byte[512];
while((readBuffer = input.read(buffer)) != -1) {
	System.out.write(buffer, 0, readBuffer);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1627581273698&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;File file = new File(&quot;C:\\Users\\world\\Desktop\\javaprogramming\\FileIO\\Sample.txt&quot;);
FileReader filereader = new FileReader(file);
//입력 버퍼 생성
BufferedReader bufReader = new BufferedReader(filereader);
String line = &quot;&quot;;
while((line = bufReader.readLine()) != null){
	System.out.println(line);
}&lt;/code&gt;&lt;/pre&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;File.Seperator 운영체제에 상관없이&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;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;객체의 상태값을 일렬로 나열한 것&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. BigDecimal&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;BigInteger, BigDecimal로&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;a href=&quot;https://sejoung.github.io/2019/03/2019-03-07-Item_60_Avoid_float_and_double_if_exact_answers_are_required/&quot;&gt;https://sejoung.github.io/2019/03/2019-03-07-Item_60_Avoid_float_and_double_if_exact_answers_are_required/&lt;/a&gt;&lt;/span&gt;&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;2. 오버플로우 방지&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;205&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/naXWh/btraJy8m5XD/VHr8Uju61YOTmpbQkdTax0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/naXWh/btraJy8m5XD/VHr8Uju61YOTmpbQkdTax0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/naXWh/btraJy8m5XD/VHr8Uju61YOTmpbQkdTax0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnaXWh%2FbtraJy8m5XD%2FVHr8Uju61YOTmpbQkdTax0%2Fimg.png&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;205&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Q1 클래스에서 사용하는 변수의 3가지 종류에 대해 설명해주세요.&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로컬 변수는 메서드 내에서 선언된 위치부터 소속된 중괄호가 끝나는 지점까지 사용이 가능합니다.&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;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스 변수는 클래스의 모든 인스턴스에서 공유하는 변수로서 클래스가 메모리에 올라갈 때 static 영역에 생성됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Q2 Wrapper Class에 대해 설명해주세요.&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;8개의 기본 타입에 해당하는 데이터를 객체로 포장해주는 클래스&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Wrapper 클래스를 이용할 때 주의할 점은 참조형끼리 연산이 불가능하기 때문에 기본 자료형으로 변환하여 연산해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Q3 StringBuilder와 StringBuffer의 차이점을 설명해주세요.&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;둘의 차이점은 동기화 처리 여부입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;StringBuilder는 동기화 처리를 하지 않아 StringBuffer보다 속도가 더 빠릅니다. StringBuffer은 속도는 느리지만 멀티 스레드 환경에서 안전하게 사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&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;pre id=&quot;code_1627564898502&quot; class=&quot;java&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class A {
	String go() {
		return &quot;Go!&quot;;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1627564898502&quot; class=&quot;java&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class B extends A {
	//int go() {
	//	return 3;
	//}
	String go() {
		return &quot;3&quot;;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1627564898502&quot; class=&quot;java&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {
	public static void main(String[] args) {
		A a = new B();
		System.out.println(a.go());
	}
}&lt;/code&gt;&lt;/pre&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;컴파일 에러&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;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Go!&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3&lt;/span&gt;&lt;/li&gt;
&lt;/ol&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;pre id=&quot;code_1627581192233&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String a = &quot;Joylish&quot;;
String b = &quot;Joylis&quot;+ &quot;h&quot;;
String c = new String(&quot;Joylish&quot;);
int x = 10;&lt;/code&gt;&lt;/pre&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;a == b 는 무엇일까요?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;a + a == a+b 는 무엇일까요?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;a + x == b+x 는 무엇일까요&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 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;</description>
      <category>Java</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/87</guid>
      <comments>https://yeondube.tistory.com/entry/Java-%EC%A0%95%EB%A6%AC#entry87comment</comments>
      <pubDate>Thu, 29 Jul 2021 22:25:01 +0900</pubDate>
    </item>
    <item>
      <title>[대규모 서비스를 지탱하는 기술] 1장 정리</title>
      <link>https://yeondube.tistory.com/entry/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A5%BC-%EC%A7%80%ED%83%B1%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0-1-2%EC%9E%A5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;'대규모 서비스를 지탱하는 기술' 책을 읽고 정리하였습니다.&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;무엇을, 어떤 것을 보고 대규모 서비스라고 하는지&lt;/b&gt;' 궁금했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 서비스는 &lt;b&gt;1대의 서버로 운영할 수 없는 서비스 , 즉 서버 확장이 필요한 서비스&lt;/b&gt;라고 한다. 고객은 웹 사이트, 앱 및 플랫폼에서 항상 똑같은 서비스를 제공받길 기대한다. 책을 주문하고 있는데 갑자기 웹 사이트가 느려지거나 404 페이지가 난다면? 이는 고객 만족도를 떨어트린다. 여러 번 발생한다면? 고객은 다신 서비스를 이용하지 않거나 다른 경쟁 서비스로 이동할 수 있다. 따라서 회사 입장에선 서비스가 더 빠르게 성장하길 원하기 때문에 확장성을 고려해야할 수 밖에 없는 것이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그렇다면 서버 확장하는 방법은 무엇이 있을까&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;What is Scaling&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Scale up&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU 변경/ RAM 추가 또는 고성능 서버로 교환하여 기존 서버의 성능이나 용량 증강시켜, 더욱 처리 능력을 향상시키는 것이다.&lt;/li&gt;
&lt;li&gt;하나의 서버의 능력을 증강하기 때문에 &lt;b&gt;수직 스케일링(vertical scaling)&lt;/b&gt;이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&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;/li&gt;
&lt;li&gt;새 장비로 교체할 때, 데이터 전체의 마이그레이션 작업이 필요해 HA 구성이 아닌 이상 다운타임이 불가피하다. 저장된 데이터양에 따라 작업이 수개월 걸릴 수 있다.&lt;/li&gt;
&lt;/ul&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;/li&gt;
&lt;li&gt;추가되는 용량이나 업그레이드 비용만 부가된다.&lt;/li&gt;
&lt;li&gt;인프라의 노후화로 인한 업그레이드 주기는 이전보다 좁혀지는 추세이다. 최근에는 짧게 3년에서 최대 7년까지로 잦은 교체 주기로 인한 비용을 고려했을 때 scale out 대비 상대적으로 불리하다&lt;/li&gt;
&lt;/ul&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;Server 한 대에 모든 부하가 집중되므로 장애 시 영향을 크게 받을 수 있는 위험성이 있음&lt;/li&gt;
&lt;/ul&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;온라인 금융거래와 같이 워크플로우 기반에 빠르고 정확하면서 단순한 처리가 필요한 OLTP(Online Transaction Processing) 환경에서는 고성능의 스케일업 방식이 적합&lt;/li&gt;
&lt;li&gt;백업용 스토리지를 구축하는 데 있어서 향후 수년간 데이터 증가 폭이 미미하거나 규모가 작은 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Scale out&lt;/h4&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;사용자는 분산 파일 시스템이나 글로벌 네임스페이스(global namespace)를 통해 스토리지 서버 클러스터를 하나의 시스템으로써 인식하며, 서버를 추가로 확장하기 때문에 &lt;b&gt;수평 스케일링(horizontal scaling)&lt;/b&gt;이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;확장성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확장이 유연하다. 장기적인 용량 증가 추이를 예측할 필요 없이 그 시점에 필요한 만큼 서버를 추가해 용량과 성능을 확장할 수 있다.(pay-as-you-grow)&lt;/li&gt;
&lt;li&gt;여러 개의 서버에 부하를 균등하게 분산시키기 위해 로드 밸런싱이 필요하다&lt;/li&gt;
&lt;li&gt;복수의 서버를 연결하기 때문에 케이블링이 복잡하다&lt;/li&gt;
&lt;li&gt;개별 노드는 scale up 된 하이엔드 하드웨어보다 상대적으로 성능이나 안정성이 떨어질 수 있다.&lt;/li&gt;
&lt;/ul&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;/li&gt;
&lt;li&gt;소프트웨어 가격이 스케일업에 비해 비싸다.&lt;/li&gt;
&lt;/ul&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;Server 한 대가 장애로 다운되더라도 다른 server로 service 제공이 가능하다&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;/li&gt;
&lt;li&gt;노드가 늘어나도 성능이 증가하지는 않고 노드 증가에 따라 수용가능한 트래픽이 늘어나 대역폭은 증가해 지연이 발생할 가능성이 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;/li&gt;
&lt;li&gt;대량의 데이터 처리와 복잡한 쿼리가 이루어지는 OLAP(Online Analytical Processing) 애플리케이션 환경
&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;고민할 부분 Wrap up&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;요청을&amp;nbsp;어떻게&amp;nbsp;여러&amp;nbsp;서버에&amp;nbsp;분산할&amp;nbsp;것인가?&lt;br /&gt;DB를 분산했을 때 데이터 동기화 문제처리는 어떻게 할 것인가?&lt;br /&gt;분산 환경에서 주는 네트워크 지연시간(latency)은 어떻게 처리할까?&lt;br /&gt;서비스간 트랜잭션 처리, 장애 복구는 어떻게 할까?&lt;br /&gt;여러 대 운용은 어떻게 자동화할 것인가?&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reference&lt;/h4&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://library.gabia.com/contents/infrahosting/1222&quot;&gt;서버 확장을 위한 두 가지 방법: 스케일 아웃과 스케일 업&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tech.gluesys.com/blog/2020/02/17/storage_3_intro.html&quot;&gt;스토리지 기초 지식 3편: 스케일 업과 스케일 아웃&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jeong-pro.tistory.com/235&quot;&gt;정아마추어의 대규모 서비스를 지탱하는 기술 1~5장&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>기타 개발 스킬</category>
      <category>대규모 서비스를 지탱하는 기술</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/86</guid>
      <comments>https://yeondube.tistory.com/entry/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A5%BC-%EC%A7%80%ED%83%B1%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0-1-2%EC%9E%A5#entry86comment</comments>
      <pubDate>Thu, 29 Jul 2021 19:19:02 +0900</pubDate>
    </item>
    <item>
      <title>JVM</title>
      <link>https://yeondube.tistory.com/entry/JVM</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;JVM이 왜 등장했을까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java는&amp;nbsp;&lt;b&gt;네트워크에 연결된 모든 디바이스에서 작동&lt;/b&gt;하는 것이 목적이었다. 즉, 디바이스마다 운영체제와 하드웨어가 다르기 때문에&amp;nbsp;&lt;b&gt;플랫폼에 의존적이지 않는 언어&lt;/b&gt;를 만들고 싶었다고 한다. 그래서 Java 바이트 코드를 실행시킬 수 있는 JVM이 등장했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&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;b&gt;환경, 플랫폼 = 운영체제 + CPU&amp;nbsp;&lt;/b&gt;&lt;br /&gt;ex) 내 컴퓨터의 운영체제는 Linux야. CPU는 AMD 라이젠.&lt;/li&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;h4 data-ke-size=&quot;size20&quot;&gt;환경이 다를 경우 Java와 C/C++ 대응방식&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C/C++ : &lt;b&gt;크로스컴파일&lt;/b&gt;; 타겟 플랫폼에 맞춰 컴파일&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java : 자바 &lt;b&gt;바이트 코드&lt;/b&gt;는 &lt;b&gt;플랫폼에 상관없이 / 플랫폼 독립적으로&lt;/b&gt; JVM 위에서 동작&lt;br /&gt;주의! &lt;b&gt;JVM은 타겟 플랫폼에 의존한다&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Java를 사용해서 프로그램을 작성하려면 JDK를 설치해야 한다.&lt;br /&gt;여기서 JDK는 운영체제별 종류가 다르다. JDK안에는 JRE, API, JVM이 포함되어 있다.&lt;br /&gt;운영체제마다 다른 JVM이 제공되기 때문에 독립적일 수 있다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JVM은 무엇일까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM(자바가상머신)은 Java의 바이트 코드를 운영체제가 이해할 수 있는 기계어(명령어)로 해석한다. &lt;b&gt;JVM은 Main method가 없으면 실행되지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java로 작성한 소스코드(.java)가 컴파일&lt;/li&gt;
&lt;li&gt;Java 바이트코드(.class)파일 생성&lt;/li&gt;
&lt;li&gt;바이트 코드는 JVM을 거쳐서 기계어로 변환&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;.java 파일 :&amp;nbsp; java 규칙에 맞게 작성한, 사람이 읽을 수 있는 소스코드 파일이다&lt;br /&gt;.class 파일 :&amp;nbsp; 컴파일러에 의해 생성된 java bytecode로 구성된 파일이다.&lt;br /&gt;컴파일러 : .java파일에 오류가 있는지 검사하고 JVM을 위한 Java 바이트 코드를 생성한다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;566&quot; width=&quot;500&quot; height=&quot;342&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSHD8T/btq84Kg2PR7/kmkL3jxzr7BFD3OLIq01K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSHD8T/btq84Kg2PR7/kmkL3jxzr7BFD3OLIq01K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSHD8T/btq84Kg2PR7/kmkL3jxzr7BFD3OLIq01K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSHD8T%2Fbtq84Kg2PR7%2FkmkL3jxzr7BFD3OLIq01K0%2Fimg.png&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;566&quot; width=&quot;500&quot; height=&quot;342&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Java 소스코드가 실행되기까지&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java Compiler&lt;/li&gt;
&lt;li&gt;JIT Compiler&lt;/li&gt;
&lt;li&gt;Java Virtual Machine(JVM)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;366&quot; width=&quot;600&quot; height=&quot;256&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvAN7M/btq84stUiL6/5gtlK5VTdk9GMKVRojl1yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvAN7M/btq84stUiL6/5gtlK5VTdk9GMKVRojl1yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvAN7M/btq84stUiL6/5gtlK5VTdk9GMKVRojl1yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvAN7M%2Fbtq84stUiL6%2F5gtlK5VTdk9GMKVRojl1yk%2Fimg.png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;366&quot; width=&quot;600&quot; height=&quot;256&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JVM 구조&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;class loader subsystem&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;runtime data area&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;execution engine&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;native method interface - JNI&lt;/li&gt;
&lt;li&gt;native method libraries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1321&quot; data-filename=&quot;Untdfdsfsdftled.png&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dl1y15/btq810SCNKT/FxR0c8kkkaBnQTP17xPGY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dl1y15/btq810SCNKT/FxR0c8kkkaBnQTP17xPGY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dl1y15/btq810SCNKT/FxR0c8kkkaBnQTP17xPGY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdl1y15%2Fbtq810SCNKT%2FFxR0c8kkkaBnQTP17xPGY1%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1321&quot; data-filename=&quot;Untdfdsfsdftled.png&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Class Loader Subsystem&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;클래스 로더(Class Loader)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Runtime 시에 .jar 파일 내 저장된 클래스 파일(Java 바이트코드)을 JVM에 로드하고 사용하지 않는 Class는 메모리에서 삭제한다.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;자바는 동적 로딩(Dynamic Loading) 을 지원한다. Class Loader는 컴파일 타임에 모든 클래스가 로딩되지 않고 필요한 시점에 (런타임 도중) 해당 클래스를 실행하고 로딩한다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span&gt;Java 8 기준 ClassLoader 계층구조&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;321&quot; width=&quot;400&quot; height=&quot;206&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qbf2e/btq810yoIL0/8IBJP5kSTNTgS2uxfZWHxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qbf2e/btq810yoIL0/8IBJP5kSTNTgS2uxfZWHxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qbf2e/btq810yoIL0/8IBJP5kSTNTgS2uxfZWHxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQbf2e%2Fbtq810yoIL0%2F8IBJP5kSTNTgS2uxfZWHxk%2Fimg.png&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;321&quot; width=&quot;400&quot; height=&quot;206&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) Bootstrap ClassLoader&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;&amp;lt;JAVA_HOME&amp;gt;/jre/lib에 담긴 자바 라이브러리를 로딩한다.&lt;/li&gt;
&lt;li&gt;Native C로 구현됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) Extension ClassLoader&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;JAVA_HOME&amp;gt;/jre/lib/ext 폴더에 담긴 자바의 확장 클래스파일을 로딩한다.&lt;/li&gt;
&lt;li&gt;Java로 구현됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) Application ClassLoader&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-classpath(또는 -cp)폴더에 있는 클래스를 로딩한다.&lt;/li&gt;
&lt;li&gt;Java로 구현됨.&lt;/li&gt;
&lt;li&gt;개발자가 직접 작성한 코드를 로딩한다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위의 클래스들은 아래 3가지 원칙에 따라 동작한다.&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;Delegation Principle&lt;br /&gt;클래스 로더는 상위 클래스 로더로 로딩요청을 위임한다. 최상위 클래스로더, 부트스트랩 로더에서부터 로딩요청을 수행하고 하위 클래스로더로 요청을 넘긴다.&lt;/li&gt;
&lt;li&gt;Visibility Principle&lt;br /&gt;범위 규칙을 적용한다. 하위 클래스로더는 상위 클래스로더의 클래스를 찾을 수 있다. 하지만 상위클래스 로더는 하위클래스 로더가 로딩한 클래스를 사용할 수 없다.&lt;/li&gt;
&lt;li&gt;Uniqueness Principle&lt;br /&gt;하위 클래스로더는 상위 클래스로더가 로딩한 클래스를 다시 로딩하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Runtime Data Area ⭐&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;JVM 이 Java 바이트코드를 실행하기 위해 운영체제로부터 할당받은 메모리 공간이다. &lt;u&gt;Thread끼리 공유하는 영역인지 아닌지 여부&lt;/u&gt;에 따라 Runtime Data Area에 있는 주요 구성요소를 정리해보았다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;370&quot; width=&quot;500&quot; height=&quot;254&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8qkyf/btq8ZOZxxZZ/rTgw0mHjtpWQ1oltAeAkMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8qkyf/btq8ZOZxxZZ/rTgw0mHjtpWQ1oltAeAkMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8qkyf/btq8ZOZxxZZ/rTgw0mHjtpWQ1oltAeAkMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8qkyf%2Fbtq8ZOZxxZZ%2FrTgw0mHjtpWQ1oltAeAkMK%2Fimg.png&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;370&quot; width=&quot;500&quot; height=&quot;254&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;2-1 Thread가 공유하지 않는 영역&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1) PC Register&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드가 시작될 때 함께 생성되는 공간이다. 스레드마다 1개씩 있다.&lt;/li&gt;
&lt;li&gt;스레드가 어떤 부분을, 어떤 명령으로 실행해야 할 지 기록한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JVM은 Stacks-Base 방식으로 작동한다. 즉, JVM은 CPU에 직접 Instruction을 수행하지 않고 Stack에서 Operand를 뽑아내 이를 별도의 메모리 공간, PC Register에 저장한다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2) JVM Stack&lt;/p&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;color: #000000;&quot;&gt;Thread 제어를 위해 사용되는 메모리 영역이다. 스레드가 생성될 때마다 1개씩 생성된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;메소드 호출 시 생성되는 스레드 수행정보를 기록하는 Frame(Stack Frame)을 저장하는 공간&lt;/li&gt;
&lt;li&gt;메소드 정보, 지역변수, 매개변수, 연산 중 발생하는 임시 데이터 저장하는 공간&lt;/li&gt;
&lt;li&gt;기본(원시)타입 변수는 스택 영역에 직접 값을 가진다. 참조타입 변수는 힙 영역이나 메소드 영역의 객체 주소를 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Method 가 호출 되면 Method와 Method 정보는 Stack에 쌓이게 된다.&lt;br /&gt;Method 호출이 종료 될때 Stack point에서 제거 된다. Method 정보는 해당 Method의 매개변수, 지역변수, 임시변수,메소드 호출 한 주소 등을 저장하고 Method 종료시 메모리 공간이 사라진다.&lt;br /&gt;&lt;br /&gt;멀티 Thread 프로그램의 경우 각 Thread가 자신의 Stack을 가지고는 있지만 Heap 영역은 공유하기 때문에, 프로그래밍시에 Thread-safe 하지 않는 이슈에 주의하며 프로그래밍을 해야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3) Native Method Stack&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java가 아닌 다른 언어로 작성된 코드(C, C++ 등)에서 제공되는 Method 정보를 저장하기 위한 Stack&lt;/li&gt;
&lt;li&gt;JNI(Java Native Interface)가 해당 코드를 바이트코드로 변환하여 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;2-2 Thread가 공유하는 영역&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) Method Area/Class Area/Static Area&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222426;&quot;&gt;JVM이 읽어들인 클래스와 인터페이스에 대한 Runtime Constant Pool, 멤버 변수, 클래스 변수(Static 변수), 생성자와 메소드를 저장하는 공간이다.&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로그램 실행 중 클래스가 사용되면, &lt;b&gt;JVM&lt;/b&gt;은 클래스 파일(Java 바이트 코드)을 읽어서 분석하여 &lt;b&gt;클래스 변수, 클래스의 인스턴스 변수, 메소드 코드&lt;/b&gt; 등을 Method Area에 저장한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;프로그램이 실행되면 모든 코드가 저장되어 있는 상태가 아니다. new 키워드를 통해 객체가 동적으로 생성되기 이전에는 텍스트 일 뿐이다. 객체 생성 후에 메소드를 실행하게 되면&amp;nbsp;해당 클래스 코드에 대한 정보를 Method Area에 저장 하게 된다.&amp;nbsp;&lt;/blockquote&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #222426;&quot;&gt;클래스 변수(Static 변수)&lt;/span&gt;는 Method Area의 Class Variable에 저장한다.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;기본형이 아닌 &lt;b&gt;static 클래스형 변수는 레퍼런스 변수만 저장&lt;/b&gt;되고 &lt;b&gt;실제 인스턴스는 Heap에 저장&lt;/b&gt;되어 있다. 그 후 이 인스턴스의 변수를 저장하기 위해 Heap에 메모리가 확보가 되고 Heap의 인스턴스가 Method Area의 어느 클래스 정보와 연결되는지 설정 하게 된다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;396&quot; width=&quot;500&quot; height=&quot;232&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTDQfd/btq8ZY2jubz/mSVbIhHNAvd0OZQoxeN9v1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTDQfd/btq8ZY2jubz/mSVbIhHNAvd0OZQoxeN9v1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTDQfd/btq8ZY2jubz/mSVbIhHNAvd0OZQoxeN9v1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTDQfd%2Fbtq8ZY2jubz%2FmSVbIhHNAvd0OZQoxeN9v1%2Fimg.png&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;396&quot; width=&quot;500&quot; height=&quot;232&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Method Area/Class Area/Static Area에 저장되는 정보&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(a) Type Information&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;클래스와 인터페이스를 Type라고 본다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Type의 전체 이름 (패키지명 + 클래스명)&lt;br /&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Type의 직계 하위 클래스 전체 이름&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Type의 클래스 / 인터페이스 여부&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Type의 modifier (public / abstract / final)&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;연관된 인터페이스 이름 리스트&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(b) &lt;b&gt;Runtime Constant Pool &lt;/b&gt;⭐&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Method Area 에 있는 독자적인 영역&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #222426;&quot;&gt;클래스 파일 constant_pool 테이블에 해당하는 영역&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상수 자료형을 저장하여 참조하고 중복을 막기 위한 공간&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Type, Field, Method로의 모든 Symbolic Reference 정보를 포함&lt;br /&gt;클래스와 인터페이스 상수, 메소드와 필드에 대한 모든 레퍼런스를 저장한다&lt;br /&gt;Constant Pool의 Entry는 배열과 같이 인덱스 번호를 통해 접근&lt;br /&gt;Object의 접근 등 모든 참조를 위한 핵심 요소&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(c) Field information : 인스턴스 변수&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Field Type&lt;br /&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Field modifier (public / private / protected / static / final / volatile / transient)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(d) Method Information : Constructor를 포함한 모든 메소드&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Method Name&lt;br /&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Method Return Type&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Method Parameter 수와 Type&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Method modifier (public / private / protected / static / final / syncronized / native / abstract)&lt;br /&gt;&lt;/span&gt;Method 구현 부분이 있을 경우 ( abstract 또는 native 가 아닐 경우)&lt;br /&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Method의 byteCode&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Method의 Stack Frame의 Operand Stack 및 Local variable section의 크기&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;Exception Table&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(e) Class Variable : static 키워드로 선언된 변수&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;/li&gt;
&lt;li&gt;final class 변수는 상수로 치환 되어 Runtime Constant Pool에 값을 복사한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;5) Heap&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;객체를 저장하는 가상 메모리 공간 (Method Area 는 클래스를 위한 공간)&lt;/li&gt;
&lt;li&gt;new 연산자로 생성된 객체와 배열을 저장한다. 이때, Class Area에 올라온 클래스들만 객체로 생성할 수 있다.&lt;/li&gt;
&lt;li&gt;힙 영역에 생성된 객체와 배열은 스택 영역의 변수나 다른 객체의 필드에서 참조한다. 참조하는 변수나 필드가 없다면 의미 없는 객체가 되어 GC의 대상이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;347&quot; width=&quot;500&quot; height=&quot;190&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOJNmX/btq85mNzP33/2w2GO78RYb33aHJQHJHCF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOJNmX/btq85mNzP33/2w2GO78RYb33aHJQHJHCF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOJNmX/btq85mNzP33/2w2GO78RYb33aHJQHJHCF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOJNmX%2Fbtq85mNzP33%2F2w2GO78RYb33aHJQHJHCF0%2Fimg.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;347&quot; width=&quot;500&quot; height=&quot;190&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(a) Young Generation&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eden : 객체들이 최초로 생성되는 공간&lt;/li&gt;
&lt;li&gt;Survivor 0 / 1 : Eden 에 의해 참조되는 객체들이 저장되는 공간&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(b) Old Generation&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 불가능 상태로 되지 않아 Young 영역에서 살아남은 객체들이 저장되는 공간&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(c) Permanent Generation / Method Area&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;Class Loader 에 의해 로드되는 Class, Method 등에 대한 메타 데이터가 저장되는 영역. (해당 정보는 JVM에 의해 사용된다)&lt;/li&gt;
&lt;li&gt;Reflection을 사용해 동적으로 클래스가 로딩 되는 경우에 사용&lt;br /&gt;&amp;rarr; 내부적으로 Reflection 을 자주 사용하는 Spring Framework를 사용한다면 이 영역에 대한 고려가 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❗❕ &lt;a href=&quot;https://johngrib.github.io/wiki/java8-why-permgen-removed/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Java 8부터는 &lt;b&gt;Permanent 영역대신 Metaspcae 로 호출되는 네이티브 영역에 저장&lt;/b&gt;됩니다.&lt;/a&gt;&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;3. Execution Engine&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class Loader가 JVM에 로드한 클래스 파일(Java 바이트 코드)를 실행한다. &lt;span style=&quot;color: #000000;&quot;&gt;Execution Engine을 통해서 Machine이 읽을 수 있는 형태로 ByteCode를 변환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;209&quot; width=&quot;500&quot; height=&quot;118&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caLByK/btq85lOQEQy/6QUTJmHdAK5XbGiPXTFFV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caLByK/btq85lOQEQy/6QUTJmHdAK5XbGiPXTFFV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caLByK/btq85lOQEQy/6QUTJmHdAK5XbGiPXTFFV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaLByK%2Fbtq85lOQEQy%2F6QUTJmHdAK5XbGiPXTFFV1%2Fimg.png&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;209&quot; width=&quot;500&quot; height=&quot;118&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Execution Engine 구성 요소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1) Interpreter&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Execution Engine은 Java 바이트 코드를 한 줄 씩(명령어 단위로) 해석해서 실행한다.&lt;/li&gt;
&lt;li&gt;한 줄 씩 읽어서 속도가 느리다는 단점이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2) JIT(Just - In - Time) 컴파일러 / 동적 번역(Dynamic Translation)&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;Java 바이트코드를 실행(이하 인터프리팅)하는 시점에 JIT 컴파일러는 Java 바이트코드를 컴파일하면서 변환된 기계어를 캐시에 저장한다. 한 번 컴파일된 이후에는 캐시에 보관된 기계어를 사용하기 때문에 바뀐 부분만 컴파일하여 빠르게 수행할 수 있다.&lt;/li&gt;
&lt;li&gt;Java 바이트코드를 기계어 변환하는 데에도 비용이 들기 때문에 JVM은 모든 코드를 JIT 컴파일러 방식으로 실행하지 않는다. 인터프리터 방식을 사용하다 일정 기준이 넘어가면 JIT 컴파일 방식으로 명령어를 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3) Garbage Collector&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RuntimeDataArea의 Heap 영역의 더 이상 참조되지 않는 객체를 해제하는 방식으로 메모리를 관리한다&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;/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://asfirstalways.tistory.com/158&quot;&gt;https://asfirstalways.tistory.com/158&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.holaxprogramming.com/2013/07/16/java-jvm-runtime-data-area/&quot;&gt;https://www.holaxprogramming.com/2013/07/16/java-jvm-runtime-data-area/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Execution Engine : &lt;a href=&quot;https://www.slipp.net/wiki/pages/viewpage.action?pageId=8880270&quot;&gt;https://www.slipp.net/wiki/pages/viewpage.action?pageId=8880270&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;JVM의 실행 엔진 : &lt;a href=&quot;https://www.javacodegeeks.com/2018/04/jvm-architecture-execution-engine-in-jvm.html&quot;&gt;https://www.javacodegeeks.com/2018/04/jvm-architecture-execution-engine-in-jvm.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;JIT 컴파일 : &lt;a href=&quot;https://ko.wikipedia.org/wiki/JIT_%EC%BB%B4%ED%8C%8C%EC%9D%BC&quot;&gt;https://ko.wikipedia.org/wiki/JIT_%EC%BB%B4%ED%8C%8C%EC%9D%BC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;JIT 컴파일러 : &lt;a href=&quot;https://medium.com/@ahn428/java-jit-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC-c7d068e29f45&quot;&gt;https://medium.com/@ahn428/java-jit-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC-c7d068e29f45&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>Java</category>
      <category>jvm</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/85</guid>
      <comments>https://yeondube.tistory.com/entry/JVM#entry85comment</comments>
      <pubDate>Wed, 7 Jul 2021 19:18:57 +0900</pubDate>
    </item>
    <item>
      <title>[백준 9935번] 문자열폭발</title>
      <link>https://yeondube.tistory.com/entry/%EB%B0%B1%EC%A4%80-9935%EB%B2%88-%EB%AC%B8%EC%9E%90%EC%97%B4%ED%8F%AD%EB%B0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9935&quot;&gt;https://www.acmicpc.net/problem/9935&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1625429148880&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;9935번: 문자열 폭발&quot; data-og-description=&quot;첫째 줄에 문자열이 주어진다. 문자열의 길이는 1보다 크거나 같고, 1,000,000보다 작거나 같다. 둘째 줄에 폭발 문자열이 주어진다. 길이는 1보다 크거나 같고, 36보다 작거나 같다. 두 문자열은 모&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/9935&quot; data-og-url=&quot;https://www.acmicpc.net/problem/9935&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/d8b2q5/hyKMTAQSQJ/ieVkQCoVP9d9bbiy9GYwxk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9935&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/9935&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/d8b2q5/hyKMTAQSQJ/ieVkQCoVP9d9bbiy9GYwxk/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;9935번: 문자열 폭발&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 문자열이 주어진다. 문자열의 길이는 1보다 크거나 같고, 1,000,000보다 작거나 같다. 둘째 줄에 폭발 문자열이 주어진다. 길이는 1보다 크거나 같고, 36보다 작거나 같다. 두 문자열은 모&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 접근 및 해결 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 replace와 재귀를 이용해서 Java로 풀었는데 메모리 초과가 났다. 싸피분들과 리뷰를 하며 스택을 이용해야한다는 걸 알게 되었다. 백준 9935번 문자열 폭발과 유사해서 다시 풀어보았다. 마침 Go를 배우고 있어서 Go로 다시 풀어보았다. 풀면서 Go에 stack이 없어서 slice 개념을 이용해서 구현했다. 또한 풀면서 Go가 지원하는 자료형에 char이 없어서 slice한 string이 byte형이 된다는 것을 알게되었다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color:#f0f0f0;font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position:relative !important;overflow:auto&quot;&gt;&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin:0;padding:0;border:none;background-color:#272727;border-radius:4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tr&gt;&lt;td style=&quot;padding:6px;border-right:2px solid #4f4f4f&quot;&gt;&lt;div style=&quot;margin:0;padding:0;word-break:normal;text-align:right;color:#aaa;font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace !important;line-height:130%&quot;&gt;&lt;div style=&quot;line-height:130%&quot;&gt;1&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;2&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;3&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;4&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;5&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;6&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;7&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;8&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;9&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;10&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;11&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;12&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;13&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;14&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;15&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;16&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;17&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;18&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;19&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;20&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;21&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;22&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;23&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;24&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;25&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;26&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;27&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;28&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;29&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;30&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;31&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;32&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;33&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;34&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;35&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;36&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;37&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;38&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td style=&quot;padding:6px 0;text-align:left&quot;&gt;&lt;div style=&quot;margin:0;padding:0;color:#f0f0f0;font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace !important;line-height:130%&quot;&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;package&lt;/span&gt;&amp;nbsp;main&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;import&lt;/span&gt;&amp;nbsp;(&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:#ffd500&quot;&gt;&quot;bufio&quot;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:#ffd500&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:#ffd500&quot;&gt;&quot;os&quot;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;)&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;func&amp;nbsp;main()&amp;nbsp;{&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;r&amp;nbsp;:&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;bufio.NewReader(os.Stdin)&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;w&amp;nbsp;:&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;bufio.NewWriter(os.Stdout)&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;defer&amp;nbsp;w.Flush()&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;str,&amp;nbsp;target&amp;nbsp;string&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;stack&amp;nbsp;[]&lt;span style=&quot;color:#4be6fa&quot;&gt;byte&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fmt.Fscan(r,&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;&amp;amp;&lt;/span&gt;str,&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;&amp;amp;&lt;/span&gt;target)&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;lenOfStr&amp;nbsp;&lt;span style=&quot;color:#4be6fa&quot;&gt;int&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;len(str)&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;lenOfTarget&amp;nbsp;&lt;span style=&quot;color:#4be6fa&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;len(target)&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:#ff3399&quot;&gt;for&lt;/span&gt;&amp;nbsp;i&amp;nbsp;:&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#c10aff&quot;&gt;0&lt;/span&gt;;&amp;nbsp;i&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;&amp;lt;&lt;/span&gt;&amp;nbsp;lenOfStr;&amp;nbsp;i&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;+&lt;/span&gt;&amp;nbsp;{&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stack&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;append(stack,&amp;nbsp;str[i])&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;lenOfStack&amp;nbsp;&lt;span style=&quot;color:#4be6fa&quot;&gt;int&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;len(stack)&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:#ff3399&quot;&gt;if&lt;/span&gt;&amp;nbsp;stack[lenOfStack&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#c10aff&quot;&gt;1&lt;/span&gt;]&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;target[lenOfTarget&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#c10aff&quot;&gt;1&lt;/span&gt;]&amp;nbsp;{&amp;nbsp;&lt;span style=&quot;color:#999999&quot;&gt;//&amp;nbsp;만약&amp;nbsp;넣은&amp;nbsp;문자가&amp;nbsp;폭발문자열의&amp;nbsp;끝문자와&amp;nbsp;같을&amp;nbsp;경우&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&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;&lt;span style=&quot;color:#ff3399&quot;&gt;if&lt;/span&gt;&amp;nbsp;lenOfStack&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;lenOfTarget&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;&amp;amp;&lt;/span&gt;&amp;nbsp;string(stack[lenOfStack&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;-&lt;/span&gt;lenOfTarget:lenOfStack])&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;target&amp;nbsp;{&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;stack&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;stack[:lenOfStack&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;-&lt;/span&gt;lenOfTarget]&amp;nbsp;&lt;span style=&quot;color:#999999&quot;&gt;//&amp;nbsp;폭발문자열이&amp;nbsp;있으면&amp;nbsp;폭발문자열&amp;nbsp;빼주기&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&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;}&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:#ff3399&quot;&gt;if&lt;/span&gt;&amp;nbsp;len(stack)&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff3399&quot;&gt;=&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#c10aff&quot;&gt;0&lt;/span&gt;{&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fmt.Fprintln(w,&amp;nbsp;&lt;span style=&quot;color:#ffd500&quot;&gt;&quot;FRULA&quot;&lt;/span&gt;)&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;span style=&quot;color:#ff3399&quot;&gt;else&lt;/span&gt;{&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fmt.Fprintln(w,&amp;nbsp;string(stack))&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;}&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;text-align:right;margin-top:-13px;margin-right:5px;font-size:9px;font-style:italic&quot;&gt;&lt;a href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; style=&quot;color:#4f4f4ftext-decoration:none&quot;&gt;Colored by Color Scripter&lt;/a&gt;&lt;/div&gt;&lt;/td&gt;&lt;td style=&quot;vertical-align:bottom;padding:0 2px 4px 0&quot;&gt;&lt;a href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; style=&quot;text-decoration:none;color:white&quot;&gt;&lt;span style=&quot;font-size:9px;word-break:normal;background-color:#4f4f4f;color:white;border-radius:10px;padding:1px&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;</description>
      <category>Algorithm/practice</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/84</guid>
      <comments>https://yeondube.tistory.com/entry/%EB%B0%B1%EC%A4%80-9935%EB%B2%88-%EB%AC%B8%EC%9E%90%EC%97%B4%ED%8F%AD%EB%B0%9C#entry84comment</comments>
      <pubDate>Mon, 5 Jul 2021 05:02:00 +0900</pubDate>
    </item>
    <item>
      <title>Windows 에서 Go 환경 세팅</title>
      <link>https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/go%EC%96%B8%EC%96%B4/&quot;&gt;https://www.inflearn.com/course/go%EC%96%B8%EC%96%B4/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1625421802967&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;쉽고 빠르게 끝내는 GO언어 프로그래밍 핵심 기초 입문 과정 - 인프런 | 강의&quot; data-og-description=&quot;구글 서버, Docker, 우버등이 Go언어를 활용한 대표 프로젝트이고 최근에는 데이터 분석 및 블록체인에서도 많이 활용하고 있어 Go언어의 인기는 계속해서 수직 상승할 것으로 예상됩니다., 쉽고 &quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/go%EC%96%B8%EC%96%B4/&quot; data-og-url=&quot;https://www.inflearn.com/course/go%EC%96%B8%EC%96%B4/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/7QJIS/hyKMQKSVhY/KH4kzKA0kbzfeZIQwSPRH0/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bvKWPk/hyKMQ5bmcX/Zp4h5eN8pu0LlPQ2nlvsK1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/ca45XZ/hyKMGO1V65/9uljr4DiL2DDflY3TzuOQK/img.jpg?width=857&amp;amp;height=444&amp;amp;face=0_0_857_444&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/go%EC%96%B8%EC%96%B4/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/go%EC%96%B8%EC%96%B4/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/7QJIS/hyKMQKSVhY/KH4kzKA0kbzfeZIQwSPRH0/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bvKWPk/hyKMQ5bmcX/Zp4h5eN8pu0LlPQ2nlvsK1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/ca45XZ/hyKMGO1V65/9uljr4DiL2DDflY3TzuOQK/img.jpg?width=857&amp;amp;height=444&amp;amp;face=0_0_857_444');&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;쉽고 빠르게 끝내는 GO언어 프로그래밍 핵심 기초 입문 과정 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;구글 서버, Docker, 우버등이 Go언어를 활용한 대표 프로젝트이고 최근에는 데이터 분석 및 블록체인에서도 많이 활용하고 있어 Go언어의 인기는 계속해서 수직 상승할 것으로 예상됩니다., 쉽고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(다음 강의를 듣고 학습하였습니다.)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0. Go, Atom, Git 다운로드&lt;/h2&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://golang.org/dl/&quot;&gt;Go 다운로드 받기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://atom.io/&quot;&gt;Atom editor 다운로드 받기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/downloads&quot;&gt;git 다운로드 받기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Go 설치할 때 Go 경로는 기본적으로 주어지는 &lt;b&gt;C:\Program Files\Go\&lt;/b&gt; 로 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;262&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIC0VO/btq8OiSSJng/rFXrVNlI1fEuTqxTkKQGBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIC0VO/btq8OiSSJng/rFXrVNlI1fEuTqxTkKQGBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIC0VO/btq8OiSSJng/rFXrVNlI1fEuTqxTkKQGBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIC0VO%2Fbtq8OiSSJng%2FrFXrVNlI1fEuTqxTkKQGBk%2Fimg.png&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;262&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Go 개발환경(공간) 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;C:\workspace\go-study\ &lt;/b&gt;로 Go 개발환경(공간)을 만들었습니다. 그리고 &lt;b&gt;bin, pkg, src&lt;/b&gt; 폴더를 만들어 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;704&quot; width=&quot;500&quot; height=&quot;391&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd0xCG/btq8NusvmId/ARDkuAzegbbAlEi7yESFbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd0xCG/btq8NusvmId/ARDkuAzegbbAlEi7yESFbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd0xCG/btq8NusvmId/ARDkuAzegbbAlEi7yESFbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd0xCG%2Fbtq8NusvmId%2FARDkuAzegbbAlEi7yESFbk%2Fimg.png&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;704&quot; width=&quot;500&quot; height=&quot;391&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Go 개발환경(공간) 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Go 개발환경을 생성한 다음에 시스템에 Go 개발환경관련해서 설정해주어야합니다. &lt;b&gt;시스템 환경 변수 편집&lt;/b&gt;에 들어가서 GOPATH, GOBIN를 설정해줍니다. &amp;nbsp;&lt;a href=&quot;https://dave.cheney.net/2013/06/14/you-dont-need-to-set-goroot-really&quot;&gt;You don&amp;rsquo;t need to set GOROOT, really &lt;/a&gt;를 참고하면, 이미&lt;span&gt;&amp;nbsp;&lt;/span&gt;go에&lt;span&gt; $GOROOT가 &lt;/span&gt;포함되어 있다고 하여 설정해주지 않았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;764&quot; width=&quot;500&quot; height=&quot;488&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pbZ8B/btq8Oj5lcPn/LRBkFUKc9uwGdE5tTYk1p1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pbZ8B/btq8Oj5lcPn/LRBkFUKc9uwGdE5tTYk1p1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pbZ8B/btq8Oj5lcPn/LRBkFUKc9uwGdE5tTYk1p1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpbZ8B%2Fbtq8Oj5lcPn%2FLRBkFUKc9uwGdE5tTYk1p1%2Fimg.png&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;764&quot; width=&quot;500&quot; height=&quot;488&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Go 시스템환경변수 확인&lt;/h2&gt;
&lt;pre id=&quot;code_1625422577659&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;go env&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 환경 변수 편집 후, cmd를 켜서 다음 명령어를 입력하여 GOBIN, GOPATH를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;697&quot; width=&quot;700&quot; height=&quot;504&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ato2g/btq8KQC6Ygz/wZpjdf6KPAkNo1WBhEvXd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ato2g/btq8KQC6Ygz/wZpjdf6KPAkNo1WBhEvXd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ato2g/btq8KQC6Ygz/wZpjdf6KPAkNo1WBhEvXd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAto2g%2Fbtq8KQC6Ygz%2FwZpjdf6KPAkNo1WBhEvXd1%2Fimg.png&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;697&quot; width=&quot;700&quot; height=&quot;504&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 GOPATH와 GOBIN이 설정한 경로와 다르다면, 다시 시스템환경변수 편집기를 켜서 사용자 변수에 해당 변수가 있는지 확인합니다. 만약 있으면 지웁니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;669&quot; width=&quot;400&quot; height=&quot;473&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5Wu85/btq8OkiSb04/tBHCLWWswHmhUjGX5t5Rfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5Wu85/btq8OkiSb04/tBHCLWWswHmhUjGX5t5Rfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5Wu85/btq8OkiSb04/tBHCLWWswHmhUjGX5t5Rfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5Wu85%2Fbtq8OkiSb04%2FtBHCLWWswHmhUjGX5t5Rfk%2Fimg.png&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;669&quot; width=&quot;400&quot; height=&quot;473&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Go</category>
      <category>go</category>
      <category>Golang</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/83</guid>
      <comments>https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85#entry83comment</comments>
      <pubDate>Mon, 5 Jul 2021 03:20:47 +0900</pubDate>
    </item>
    <item>
      <title>Windows 에서 godoc  : 'godoc'은 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.</title>
      <link>https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85-godoc%EC%9D%80-%EB%82%B4%EB%B6%80-%EB%98%90%EB%8A%94-%EC%99%B8%EB%B6%80-%EB%AA%85%EB%A0%B9-%EC%8B%A4%ED%96%89%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EB%98%90%EB%8A%94-%EB%B0%B0%EC%B9%98-%ED%8C%8C%EC%9D%BC%EC%9D%B4-%EC%95%84%EB%8B%99%EB%8B%88%EB%8B%A4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1625424051761&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;Windows 에서 Go 환경 세팅&quot; data-og-description=&quot;https://www.inflearn.com/course/go%EC%96%B8%EC%96%B4/ 쉽고 빠르게 끝내는 GO언어 프로그래밍 핵심 기초 입문 과정 - 인프런 | 강의 구글 서버, Docker, 우버등이 Go언어를 활용한 대표 프로젝트이고 최근에는..&quot; data-og-host=&quot;yeondube.tistory.com&quot; data-og-source-url=&quot;https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85&quot; data-og-url=&quot;https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iAwl4/hyKMI7aTnZ/6Kl34eTTteF3nKRArDKKt1/img.png?width=336&amp;amp;height=262&amp;amp;face=0_0_336_262,https://scrap.kakaocdn.net/dn/cRWOWq/hyKMNHpvv2/9oOuvKAXuexjhebTHaBajk/img.png?width=336&amp;amp;height=262&amp;amp;face=0_0_336_262,https://scrap.kakaocdn.net/dn/gzXHX/hyKMP6hxgm/dfwuwf07Lqh3gFaMYJ8HJ1/img.png?width=969&amp;amp;height=697&amp;amp;face=0_0_969_697&quot;&gt;&lt;a href=&quot;https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iAwl4/hyKMI7aTnZ/6Kl34eTTteF3nKRArDKKt1/img.png?width=336&amp;amp;height=262&amp;amp;face=0_0_336_262,https://scrap.kakaocdn.net/dn/cRWOWq/hyKMNHpvv2/9oOuvKAXuexjhebTHaBajk/img.png?width=336&amp;amp;height=262&amp;amp;face=0_0_336_262,https://scrap.kakaocdn.net/dn/gzXHX/hyKMP6hxgm/dfwuwf07Lqh3gFaMYJ8HJ1/img.png?width=969&amp;amp;height=697&amp;amp;face=0_0_969_697');&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;Windows 에서 Go 환경 세팅&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://www.inflearn.com/course/go%EC%96%B8%EC%96%B4/ 쉽고 빠르게 끝내는 GO언어 프로그래밍 핵심 기초 입문 과정 - 인프런 | 강의 구글 서버, Docker, 우버등이 Go언어를 활용한 대표 프로젝트이고 최근에는..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yeondube.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GOPATH, GOBIN 환경변수를 모두 설정해주었습니다. Go 패키지들을 보기위해 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;cmd에서 godoc 명령어를 입력해보았습니다.&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;'godoc'은 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. &lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;라고 나와 godoc을 다운로드 받았습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. godoc 다운로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cmd 에서 다음 명령어를 입력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1625424003546&quot; class=&quot;shell&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;go get golang.org/x/tools/cmd/godoc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여전히 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;'godoc'은 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt; 라고 나왔습니다. 그래서 시스템환경변수로 지정한 GOPATH을 직접 명시하여 cmd에서 다음 명령어 입력했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1625420726678&quot; class=&quot;shell&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;%GOPATH%/bin/godoc -http=localhost:8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어가 실행되는 것을 확인하고 시스템환경변수 Path에 제가 생성한 workspace의 bin 경로를 추가해주었습니다.&amp;nbsp; GOPATH를 추가했을 때처럼&amp;nbsp;시스템환경변수 편집기에 직접&amp;nbsp;접근하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;595&quot; width=&quot;400&quot; height=&quot;402&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cavpu1/btq8OjYzljW/troKJCKQwDJHSOcQll2HR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cavpu1/btq8OjYzljW/troKJCKQwDJHSOcQll2HR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cavpu1/btq8OjYzljW/troKJCKQwDJHSOcQll2HR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcavpu1%2Fbtq8OjYzljW%2FtroKJCKQwDJHSOcQll2HR0%2Fimg.png&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;595&quot; width=&quot;400&quot; height=&quot;402&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 Path를 더블 클릭했을 때 나오는 환경 변수 편집 공간입니다. 이전에 생성한 go 개발환경 아래에 있는 bin 폴더 경로를 추가해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;56&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3YBB3/btq8NdEvEy5/xLLVudjN9B8d8VTUi0twFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3YBB3/btq8NdEvEy5/xLLVudjN9B8d8VTUi0twFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3YBB3/btq8NdEvEy5/xLLVudjN9B8d8VTUi0twFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3YBB3%2Fbtq8NdEvEy5%2FxLLVudjN9B8d8VTUi0twFK%2Fimg.png&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;56&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;path를 추가한후에 cmd를 재실행하여&lt;span&gt; 다음 명령어를 실행하였습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625424263715&quot; class=&quot;shell&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;godoc -http=localhost:8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chrome 새창에 localhost:8080으로 들어가면 godoc을 볼 수 있었습니다. 명령어 godoc만 입력하시면 기본포트 6060으로 godoc이 실행되어 localhost:6060으로 접속하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;802&quot; width=&quot;500&quot; height=&quot;336&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ54bK/btq8QzfvJ0C/RcvKkhii4OFTHakPZJ0MN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ54bK/btq8QzfvJ0C/RcvKkhii4OFTHakPZJ0MN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ54bK/btq8QzfvJ0C/RcvKkhii4OFTHakPZJ0MN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ54bK%2Fbtq8QzfvJ0C%2FRcvKkhii4OFTHakPZJ0MN1%2Fimg.png&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;802&quot; width=&quot;500&quot; height=&quot;336&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;&lt;a href=&quot;https://pkg.go.dev/golang.org/x/tools/cmd/godoc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://pkg.go.dev/golang.org/x/tools/cmd/godoc&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1625423968256&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;godoc &amp;middot; pkg.go.dev&quot; data-og-description=&quot;Godoc extracts and generates documentation for Go programs. It runs as a web server and presents the documentation as a web page. godoc -http=:6060 Usage: godoc [flag] The flags are: -v verbose mode -timestamps=true show timestamps with directory listings &quot; data-og-host=&quot;pkg.go.dev&quot; data-og-source-url=&quot;https://pkg.go.dev/golang.org/x/tools/cmd/godoc&quot; data-og-url=&quot;https://pkg.go.dev/golang.org/x/tools/cmd/godoc&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pkg.go.dev/golang.org/x/tools/cmd/godoc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pkg.go.dev/golang.org/x/tools/cmd/godoc&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;godoc &amp;middot; pkg.go.dev&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Godoc extracts and generates documentation for Go programs. It runs as a web server and presents the documentation as a web page. godoc -http=:6060 Usage: godoc [flag] The flags are: -v verbose mode -timestamps=true show timestamps with directory listings&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pkg.go.dev&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;</description>
      <category>Go</category>
      <category>go</category>
      <category>Golang</category>
      <author>연두연두부</author>
      <guid isPermaLink="true">https://yeondube.tistory.com/82</guid>
      <comments>https://yeondube.tistory.com/entry/Windows-%EC%97%90%EC%84%9C-Go-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85-godoc%EC%9D%80-%EB%82%B4%EB%B6%80-%EB%98%90%EB%8A%94-%EC%99%B8%EB%B6%80-%EB%AA%85%EB%A0%B9-%EC%8B%A4%ED%96%89%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EB%98%90%EB%8A%94-%EB%B0%B0%EC%B9%98-%ED%8C%8C%EC%9D%BC%EC%9D%B4-%EC%95%84%EB%8B%99%EB%8B%88%EB%8B%A4#entry82comment</comments>
      <pubDate>Mon, 5 Jul 2021 02:55:04 +0900</pubDate>
    </item>
  </channel>
</rss>