2023. 5. 8. 09:34ใ์ธํ๋ฐ/์คํ๋ง ๋ถํธ - ํต์ฌ ์๋ฆฌ์ ํ์ฉ
์คํ๋ง ์ธ๋ถ ์ค์ ๋ฐฉ๋ฒ (OS ํ๊ฒฝ ๋ณ์, ์๋ฐ ์์คํ ์์ฑ) (tistory.com)
์ ๋ฒ ๊ธ์์๋ ์คํ๋ง ์ธ๋ถ ์ค์ ํ๋ ๋ฐฉ๋ฒ ์ค OS ํ๊ฒฝ ๋ณ์, ์๋ฐ ์์คํ ์์ฑ์ ์ฌ์ฉํด์ ์ฝ์ด์ค๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ดค๋ค.
์ด๋ฒ์๋ ๋๋จธ์ง ์ธ๋ถ ์ค์ ๋ฐฉ๋ฒ์ ๋ํด์ ์์ฑํ ์์ ์ด๋ค.
1๏ธโฃ ์ธ๋ถ ์ค์ ๋ฐฉ๋ฒ : ์ปค๋งจ๋ ๋ผ์ธ ์ธ์
์ปค๋งจ๋ ๋ผ์ธ ์ธ์ (Command line arguments)๋ ์ ํ๋ฆฌ์ผ์ด์ ์คํ ์์ ์ ์ธ๋ถ ์ค์ ๊ฐ์ main(args) ๋ฉ์๋์ args ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ๋ ๋ฐฉ๋ฒ์ด๋ค.
java -jar XXX.jar dataA dataB
์ด๋ฐ์์ผ๋ก ๋ค์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ์คํ์ด์ค๋ก ๊ตฌ๋ถํด์ ์ ๋ฌํ๋ฉด args์ dataA, dataB ๋ฌธ์๊ฐ ์ ๋ฌ๋๋ค.
CommandLineV1
@Slf4j
public class CommandLineV1
{
public static void main(String[] args)
{
for (String arg : args)
{
log.info("arg {}", arg);
}
}
}
์คํ ๊ฒฐ๊ณผ dataA, dataB๊ฐ ๋ก๊ทธ๋ก ์ถ๋ ฅ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ทผ๋ฐ ๋ณดํต ํ๊ฒฝ๋ณ์ ์ค์ ์ ํ ๋๋ key=vaue ํ์์ผ๋ก ์ฌ์ฉํ๊ณ , ๋ฐ๋ ๊ฒ์ด ๋ ํธ๋ฆฌํ๊ณ ์ค์ ๋ก ๊ทธ๋ ๊ฒ ๋ ๋ง์ด ์ฌ์ฉ๋๋ค.
๋ฐ๋ผ์ dataA dataB ์ด๋ ๊ฒ ์ง์ ํ๋ ๊ฒ ๋ง๊ณ url=devdb username=dev_user password=dev_pw ์ด๋ฐ์์ผ๋ก ๋ฐ๊ฟ์ ๋ค์ ์คํํด๋ณด๋ฉด
key=vaue ํ์์ด ์๋๋ผ ๊ทธ๋ฅ ๊ทธ๋๋ก ์ ์ ๋ฌธ์๊ฐ ์ถ๋ ฅ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ทธ๋ฌ๋ฉด ์ด๊ฑธ ๊ฐ๋ฐ์๊ฐ ์ง์ ๋ถ๋ฆฌํด์ key=value ํ์์ผ๋ก ๋ง๊ฒ ๋ณํํด์ค์ผ ํ๋ ๋ฒ๊ฑฐ๋ฌ์์ด ์๋ค.
2๏ธโฃ ์ธ๋ถ์ค์ ๋ฐฉ๋ฒ : ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์
์ผ๋ฐ์ ์ธ ์ปค๋งจ๋ ๋ผ์ธ ์ธ์๋ ๋์ด์ฐ๊ธฐ๋ก ๊ตฌ๋ถํ๊ณ key=value ํ์์ ๊ตฌ๋ถํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.
๋ฐ๋ผ์ ์ปค๋งจ๋ ๋ผ์ธ ์ธ์๋ฅผ key=value ํ์์ผ๋ก ๊ตฌ๋ถํ๋ ๋ฐฉ๋ฒ์ด ํ์ํ๋๋ฐ ์คํ๋ง์ ์ด์ ์ปค๋งจ๋ ๋ผ์ธ ์ธ์๋ฅผ key=value ํ์์ผ๋ก ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์คํ๋ง ๋ง์ ํ์ค ๋ฐฉ์์ ์ ์ํ๋ค. -> ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์
์คํ๋ง์ ์ปค๋งจ๋ ๋ผ์ธ์ -- ๋ฅผ ์ฐ๊ฒฐํด์ ์์ํ๋ฉด key=value ํ์์ผ๋ก ์ ํ๊ณ ์ด๊ฑธ ์ปค๋งจ๋ ๋ผ์ธ ์ต์
์ธ์๋ผ๊ณ ํ๋ค.
--url=devdb --username=dev_user --password=dev_pw
(ํ๋์ ํค์ ์ฌ๋ฌ๊ฐ ์ค์ ๊ฐ๋ฅ)
CommandLineV2
@Slf4j
public class CommandLineV2
{
// --url=devdb --username=dev_user --password=dev_pw mode=on
public static void main(String[] args)
{
for (String arg : args)
{
log.info("arg {}", arg);
}
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
log.info("SourceArgs = {}", List.of(applicationArguments.getSourceArgs()));
log.info("NonOptionArgs = {}", applicationArguments.getNonOptionArgs());
log.info("OptionsNames = {} ", applicationArguments.getOptionNames());
Set<String> optionNames = applicationArguments.getOptionNames();
for (String optionName : optionNames)
{
log.info("option arg {}={}", optionName, applicationArguments.getOptionValues(optionName));
}
List<String> url = applicationArguments.getOptionValues("url");
List<String> username = applicationArguments.getOptionValues("username");
List<String> password = applicationArguments.getOptionValues("password");
List<String> mode = applicationArguments.getOptionValues("mode");
log.info("url = {}", url);
log.info("username = {}", username);
log.info("password = {}", password);
log.info("mode = {}", mode);
}
}
์คํ๋ง์ด ์ ๊ณตํ๋ ApplicationArguments ์ธํฐํ์ด์ค์ ์ด๋ฅผ ๊ตฌํํ DefaultApplicationArguments ๋ฅผ ์ฌ์ฉํ๋ฉด ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์๋ฅผ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
--urldb=devdb --username=dev_user --password=dev_pw mode=on ์ ์ฌ์ฉํ๋๋ฐ ์ด๋ mode ๋ ์์ --๋ฅผ ๋ถ์ฌ์ฃผ์ง ์์๋ค.
NonOptionArgs ๋ฅผ ์ฌ์ฉํ๋ฉด --๋ฅผ ์ฌ์ฉํ์ง ์์ ์ฆ, ์ต์ ์ธ์๊ฐ ์๋ ๊ฒ์ ์กฐํํ ์ ์๊ณ ,
OptionNames๋ฅผ ํ๋ฉด key ๊ฐ์, OptionValue(key) ๋ฅผ ํ๋ฉด value ๊ฐ์ ์กฐํํ ์ ์๋ค.
arg : ์ปค๋งจ๋ ๋ผ์ธ์ ์ ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋๋ก ์ถ๋ ฅํ๋ค.
SourceArgs : ์ปค๋งจ๋ ๋ผ์ธ ์ธ์ ์ ๋ถ ์ถ๋ ฅ
* ์ต์ ์ธ์๋ --username=dev1 --username=dev2 ์ฒ๋ผ ํ๋์ ํค์ ์ฌ๋ฌ ๊ฐ์ ๋ด์ ์ ์๊ธฐ ๋๋ฌธ์ applicationArguments.getOptionsValue(key) ์ ๊ฒฐ๊ณผ๊ฐ List๋ฅผ ๋ฐํํ๋ค.
(๊ทธ๋ฆฌ๊ณ ์ฐธ๊ณ ๋ก ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์๋ ์๋ฐ ์ธ์ด์ ํ์ค ๊ธฐ๋ฅ์ด ์๋๊ณ , ์คํ๋ง์ด ํธ๋ฆฌํจ์ ์ํด ์ ๊ณตํ๋ ๊ธฐ์ ์ด๋ค.)
๐ ์คํ๋ง ๋ถํธ๋ ์ปค๋งจ๋ ๋ผ์ธ์ ํฌํจํด์ ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์๋ฅผ ํ์ฉํ ์ ์๋ ApplicationArguments ๋ฅผ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํด๋๊ณ , ํด๋น ๋น์ ์ฃผ์ ๋ฐ์ผ๋ฉด ์ปค๋งจ๋ ๋ผ์ธ์ผ๋ก ์ ๋ ฅํ ๊ฐ์ ์ด๋์๋ ์ฌ์ฉํ ์ ์๋ค.
CommandLineBean
@Slf4j
@Component
public class CommandLineBean
{
private final ApplicationArguments arguments;
public CommandLineBean(ApplicationArguments arguments)
{
this.arguments = arguments;
}
@PostConstruct
public void init()
{
log.info("source {}", List.of(arguments.getSourceArgs()));
log.info("optionNames {}", arguments.getOptionNames());
Set<String> optionNames = arguments.getOptionNames();
for (String optionName : optionNames)
{
log.info("option args {}={}", optionName, arguments.getOptionValues(optionName));
}
}
}
3๏ธโฃ ์ธ๋ถ ์ค์ ๋ฐฉ๋ฒ : ์คํ๋ง ํตํฉ
์ง๊ธ๊น์ง ์ธ๋ถ ์ค์ ๋ฐฉ๋ฒ์ผ๋ก OS ํ๊ฒฝ ๋ณ์, ์๋ฐ ์์คํ ์์ฑ, ์ปค๋งจ๋ ๋ผ์ธ ์ธ์, ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์ ๋ฑ์ ๋ดค๋๋ฐ ๋๊ฒ(?) ๋ณด๋ฉด ์ธ๋ถ ์ค์ ๋ฐฉ๋ฒ์ key=value ํ์์ด๊ณ ์ด ์ค์ ๊ฐ์ ์ธ๋ถ์ ์ง์ ํด๋ ๊ฒ์ด๋ค.
์์์ 4๊ฐ์ง ๋ณธ ๊ฒ์ฒ๋ผ ์ธ๋ถ ์ค์ ๊ฐ ํ์์ ๊ฐ์๋ฐ ์ด๋์ ์ง์ ํด๋๋๋์ ๋ฐ๋ผ์ ๊ฐ๊ฐ ์ฝ๋ ๋ฐฉ๋ฒ์ด ๋ค๋ฅด๋ค๋ ๋จ์ ์ด ์๋ค.
์๋ฅผ ๋ค์ด์ OS ํ๊ฒฝ ๋ณ์๋ก ์ค์ ๊ฐ ์ง์ ํด๋๋ค๋ฉด System.getenv(key) ๋ฅผ ์ฌ์ฉํด์ผ ํ๊ณ , ์๋ฐ ์์คํ ์์ฑ์ผ๋ก ์ธ๋ถ๊ฐ ์ค์ ํ๋ค๋ฉด
System.getProperty(key) ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค...! ๋ง์ฝ ์ง๊ธ์ ์๋ฐ ์์คํ ์์ฑ์ ์ธ๋ถ ์ค์ ๊ฐ์ ์ง์ ํด์ ์ฌ์ฉํ๊ณ ์์๋ค๊ณ ํ๋ค.
๊ทธ๋ฐ๋ฐ ์ด์ ์ ์ฑ ๋ณ๊ฒฝ์ผ๋ก ์ธํด์ ๋ค์๋ถํฐ๋ ์ปค๋งจ๋ ๋ผ์ธ ์ธ์๋ก ์ค์ ๊ฐ์ ์ง์ ํด์ ์ฌ์ฉํ๊ธฐ๋ก ๋ฐ๊ฟจ๋ค๊ณ ํ๋ค๋ฉด...
์ด์ ์ด ์ฝ๋๋ค์ ๋ชจ๋ ๋ฐ๊ฟ์ค์ผ ํ๋ค๋ ๋ฒ๊ฑฐ๋ก์์ด ์กด์ฌํ๋ค.
-> ์ธ๋ถ ์ค์ ๊ฐ์ด ์ด๋์ ์์นํ๋ ์๊ด์์ด ์ผ๊ด์ฑ ์๊ณ , ํธ๋ฆฌํ๊ฒ key=value ํ์์ ์ธ๋ถ ์ค์ ๊ฐ์ ์ฝ์ ์ ์์ผ๋ฉด ์ฝ๋ ๋ณ๊ฒฝ์ ํ์ง ์์๋ ๋๋๊น ๊ฐ๋ฐ์ ์ ์ฅ์์๋ ๋ ํธ๋ฆฌํ๊ณ ์ธ๋ถ ์ค์ ๊ฐ ์ค์ ํ๋ ๋ฐฉ๋ฒ๋ ๋ ์ ์ฐํด์ง๋ค.
์คํ๋ง์ Environment์ PropertySource ๋ฅผ ํตํด ์ถ์ํ๋ฅผ ํด์ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค!
PropertySource
์คํ๋ง์ PropertySource ๋ผ๋ ์ถ์ ํด๋์ค๋ฅผ ์ ๊ณตํ๊ณ ๊ฐ๊ฐ ์ธ๋ถ ์ค์ ์ ์กฐํํ๋ ๊ตฌํ์ฒด๋ค์ ๋ง๋ค์๋ค.
CommandLinePropertySource
SystemEnviornmentPropertySource
...
์ด๋ ๊ฒ ์คํ๋ง์ ๋ก๋ฉ ์์ ์ ํ์ํ PropertySource๋ค์ ์์ฑํ๊ณ Environment์์ ์ด๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ์ฐ๊ฒฐํด์ค๋ค.
Environment
/**
* Interface representing the environment in which the current application is running.
* Models two key aspects of the application environment: <em>profiles</em> and
* <em>properties</em>. Methods related to property access are exposed via the
* {@link PropertyResolver} superinterface.
*
* <p>A <em>profile</em> is a named, logical group of bean definitions to be registered
* with the container only if the given profile is <em>active</em>. Beans may be assigned
* to a profile whether defined in XML or via annotations; see the spring-beans 3.1 schema
* or the {@link org.springframework.context.annotation.Profile @Profile} annotation for
* syntax details. The role of the {@code Environment} object with relation to profiles is
* in determining which profiles (if any) are currently {@linkplain #getActiveProfiles
* active}, and which profiles (if any) should be {@linkplain #getDefaultProfiles active
* by default}.
*
* <p><em>Properties</em> play an important role in almost all applications, and may
* originate from a variety of sources: properties files, JVM system properties, system
* environment variables, JNDI, servlet context parameters, ad-hoc Properties objects,
* Maps, and so on. The role of the {@code Environment} object with relation to properties
* is to provide the user with a convenient service interface for configuring property
* sources and resolving properties from them.
*
* <p>Beans managed within an {@code ApplicationContext} may register to be {@link
* org.springframework.context.EnvironmentAware EnvironmentAware} or {@code @Inject} the
* {@code Environment} in order to query profile state or resolve properties directly.
*
* <p>In most cases, however, application-level beans should not need to interact with the
* {@code Environment} directly but instead may have to have {@code ${...}} property
* values replaced by a property placeholder configurer such as
* {@link org.springframework.context.support.PropertySourcesPlaceholderConfigurer
* PropertySourcesPlaceholderConfigurer}, which itself is {@code EnvironmentAware} and
* as of Spring 3.1 is registered by default when using
* {@code <context:property-placeholder/>}.
*
* <p>Configuration of the {@code Environment} object must be done through the
* {@code ConfigurableEnvironment} interface, returned from all
* {@code AbstractApplicationContext} subclass {@code getEnvironment()} methods. See
* {@link ConfigurableEnvironment} Javadoc for usage examples demonstrating manipulation
* of property sources prior to application context {@code refresh()}.
*
* @author Chris Beams
* @since 3.1
* @see PropertyResolver
* @see EnvironmentCapable
* @see ConfigurableEnvironment
* @see AbstractEnvironment
* @see StandardEnvironment
* @see org.springframework.context.EnvironmentAware
* @see org.springframework.context.ConfigurableApplicationContext#getEnvironment
* @see org.springframework.context.ConfigurableApplicationContext#setEnvironment
* @see org.springframework.context.support.AbstractApplicationContext#createEnvironment
*/
public interface Environment extends PropertyResolver {
/**
* Return the set of profiles explicitly made active for this environment. Profiles
* are used for creating logical groupings of bean definitions to be registered
* conditionally, for example based on deployment environment. Profiles can be
* activated by setting {@linkplain AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
* "spring.profiles.active"} as a system property or by calling
* {@link ConfigurableEnvironment#setActiveProfiles(String...)}.
* <p>If no profiles have explicitly been specified as active, then any
* {@linkplain #getDefaultProfiles() default profiles} will automatically be activated.
* @see #getDefaultProfiles
* @see ConfigurableEnvironment#setActiveProfiles
* @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
*/
String[] getActiveProfiles();
/**
* Return the set of profiles to be active by default when no active profiles have
* been set explicitly.
* @see #getActiveProfiles
* @see ConfigurableEnvironment#setDefaultProfiles
* @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
*/
String[] getDefaultProfiles();
/**
* Return whether one or more of the given profiles is active or, in the case of no
* explicit active profiles, whether one or more of the given profiles is included in
* the set of default profiles. If a profile begins with '!' the logic is inverted,
* i.e. the method will return {@code true} if the given profile is <em>not</em> active.
* For example, {@code env.acceptsProfiles("p1", "!p2")} will return {@code true} if
* profile 'p1' is active or 'p2' is not active.
* @throws IllegalArgumentException if called with zero arguments
* or if any profile is {@code null}, empty, or whitespace only
* @see #getActiveProfiles
* @see #getDefaultProfiles
* @see #acceptsProfiles(Profiles)
* @deprecated as of 5.1 in favor of {@link #acceptsProfiles(Profiles)}
*/
@Deprecated
boolean acceptsProfiles(String... profiles);
/**
* Return whether the {@linkplain #getActiveProfiles() active profiles}
* match the given {@link Profiles} predicate.
*/
boolean acceptsProfiles(Profiles profiles);
}
์คํ๋ง์ Environment ๋ฅผ ์ ๊ณตํด์ ํน์ ์ธ๋ถ ์ค์ ์ ์ข ์๋์ง ์๊ณ ์ผ๊ด์ฑ์๊ฒ key=value ํ์์ ์ธ๋ถ ์ค์ ์ ์ ๊ทผํ ์ ์๋ค.
environment.getProperty(key)๋ฅผ ํตํด์ ๊ฐ์ ์กฐํํ ์ ์๊ณ , Environment๊ฐ ๋ด๋ถ์์ ์ฌ๋ฌ ๊ณผ์ ์ ๊ฑฐ์ณ์ PropertySource์ ์ ๊ทผํ๋ค.
๋ฐ๋ผ์ ์ด์ ๋ชจ๋ ์ธ๋ถ ์ค์ ์ Environment ๋ฅผ ํตํด์ ์กฐํํ๋ฉด ๋๋ค.
@Slf4j
@Component
public class EnvironmentCheck
{
private final Environment environment;
public EnvironmentCheck(Environment environment)
{
this.environment = environment;
}
@PostConstruct
public void init()
{
String url = environment.getProperty("url");
String username = environment.getProperty("username");
String password = environment.getProperty("password");
log.info("environment url = {}", url);
log.info("environment username = {}", username);
log.info("environment password = {}", password);
}
}
์๋ฐ ์์คํ ์์ฑ
-Durl=devdb -Dusername=dev_user -Dpassword=dev_pw
์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์
--url=devdb --username=dev_user --password=dev_pw
๋ก ๊ฐ๊ฐ ํ๋ฒ์ฉ ์ง์ ํด์ ์คํํด๋ณด๋ฉด ๋๋ค ๋์ผํ๊ฒ Environment ๋ฅผ ํตํด์ ์ค์ ๊ฐ์ ์ฝ์ ์ ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ฐ๋ผ์ ์ด์ Environment๋ก ์ค์ ๊ฐ์ ์ฝ์ผ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ์ธ๋ถ ์ค์ ๊ฐ์ด ์ง์ ํ๋ ๊ณณ์ด ๋ฐ๋์ด๋ ์ฝ๋ ๋ณ๊ฒฝ์ ํ์ง ์์๋ ๋๋ค!
* ์ฐ์ ์์
๋ง์ฝ ์๋ฐ ์์คํ ์์ฑ๊ณผ ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์์ ๋์์ ์ค์ ๊ฐ์ ์ง์ ํด๋๋ค๋ฉด
์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์๋ก ์ง์ ํ ์ธ๋ถ ์ค์ ๊ฐ์ ์กฐํํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ฐ์ ์์๋ ๋ณดํต ๋ ๋ฒ์๊ฐ ์ข์ ๊ฒ์ด ์ฐ์ ๊ถ์ ๊ฐ์ง๊ฒ ๋๋๋ฐ ์๋ฐ ์์คํ ์์ฑ์ ํด๋น JVM ๋ด์์ ๋ชจ๋ ์ ๊ทผ ํ ์ ์์ง๋ง, ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์๋ main์ arg๋ฅผ ํตํด์ ๋ค์ด์ค๊ธฐ ๋๋ฌธ์ ์ ๊ทผ ๋ฒ์๊ฐ ๋ ์ข๊ธฐ ๋๋ฌธ์ ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์๊ฐ ์ฐ์ ๊ถ์ ๊ฐ์ง๋ค.
'์ธํ๋ฐ > ์คํ๋ง ๋ถํธ - ํต์ฌ ์๋ฆฌ์ ํ์ฉ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์คํ๋ง ์ธ๋ถ ์ค์ ๋ฐฉ๋ฒ (์ค์ ํ์ผ ์ฐ์ ์์) (0) | 2023.05.10 |
---|---|
์คํ๋ง ์ธ๋ถ ์ค์ ๋ฐฉ๋ฒ (์ธ๋ถ ๋ฐ์ดํฐ) (0) | 2023.05.09 |
์คํ๋ง ์ธ๋ถ ์ค์ ๋ฐฉ๋ฒ (OS ํ๊ฒฝ ๋ณ์, ์๋ฐ ์์คํ ์์ฑ) (0) | 2023.05.04 |
์คํ๋ง ๋ถํธ - ์๋ ๊ตฌ์ฑ ๋์ ์๋ฆฌ (0) | 2023.05.02 |
์๋ ๊ตฌ์ฑ Auto Configuration ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ง๋ค๊ณ ์ฌ์ฉํด๋ณด๊ธฐ (0) | 2023.04.28 |