안녕하세요.

프로그래밍을 하다보면, 경로에 관한 것을 처리하곤 합니다.

이때 어떤 API절대 경로로 처리를, 또 다른 API상대 경로 로 처리를 하곤 합니다.

절대 경로 , 상대 경로의 개념이 확실하지 않으면 생각보다 프로그래밍하기 까다롭습니다.

그래서, 오늘은 프로그래밍에서 자주 쓰이는 개념인 절대 경로상대 경로에 대해 포스팅 합니다.

절대 경로란?

컴퓨터의 파일을 찾아가는 방법은 절대 경로로 파일을 찾는 방법 한가지 뿐입니다.

절대 경로란 최초의 시작점으로 경유한 경로를 전부 기입하는 방식입니다.

윈도우 OS의 바탕화면에 위치한 test.txt 파일을 예로 들어봅시다.

test.txt파일의 절대 경로는 최상위 루트 디렉토리인 C 디렉토리 로부터 시작되어 아래와 같이 구성됩니다.

C:\Users\UserID\Desktop\test.txt

어떠한 OS던 이렇게 최상위 루트 로부터 경유한 경로를 전부 기입한 절대 경로로만 파일을 찾을 수 있습니다.

그렇다면, 상대 경로는 왜 탄생했으며 어떻게 파일을 찾는 걸까요?

상대 경로란?

상대 경로상대적 이라는 단어로 개념을 설명해보려 합니다.

네이버 단어 사전에 나와 있는 상대적 은 아래와 같습니다.

서로 맞서거나 비교되는 관계에 있는. 또는 그런 것.

여기서 중요한건 비교 관계 입니다. 상대 경로는 항상 비교할 대상이 있어야 합니다.

즉, 우리가 프로그래밍을 할 때도, 상대 경로를 읽을 때도 어떤 경로로 부터 비교하는지를 염두해야 합니다.

예로 아래와 같은 상대경로 두개로 설명하려 합니다.

  • Desktop\test.txt

  • test.txt

첫번째 상대 경로는 무얼 뜻하며, 두번째 상대 경로는 무얼 뜻하는 걸까요?

확실한건, OS에게 저렇게 경로를 알려준다면 OS는 파일의 위치를 찾지 못합니다.

앞서 말했드시 OS는 절대 경로로 파일을 읽는 방법 밖에 없습니다.

위의 2개의 상대 경로C:\Users\UserID\Desktop\test.txt 를 의미할 거 같지만 비교 대상이 누구인지에 따라, 경로가 천차만별로 바뀝니다.

상대 경로의 비교 대상이 만약, C 드라이버의 Mommoo 디렉토리라면,

위의 2개의 상대 경로는 아래와 같이 절대 경로로 해석됩니다.

  • C:\Mommoo\Desktop\test.txt

  • C:\Mommoo\test.txt

우리 머리속에 담아져 있는 C:\Users\UserID\Desktop\test.txt 경로라는 아예 다른걸 볼 수 있습니다.

결론적으로, 상대 경로기준 경로를 기준으로 절대 경로가 구성되며, 상대 경로로 파일의 위치를 찾을 수 있는 이유는 기준 경로절대 경로로 변환하여 OS에게 전달 하기 때문입니다.

상대 경로가 왜 필요할까?

절대 경로는 정적인 문자열로 특정 컴퓨터의 파일 위치를 정확히 알려줍니다.

하지만, 경로를 다룰때는 이러한 정적인 특징이 오히려 단점으로 다가올 수도 있습니다.

예를들어, test.txt의 경로가 항상 자주 바뀌는 상황이거나 루트 디렉토리가 서로 다른 OS들을 다룬다면 어떨까요?

정적인 특징 때문에 전자는 절대 경로로 작성된 모든 문서를 다시 작성해야 하며, 후자는 OS 별로 절대 경로를 작성해서 관리해야 해야 합니다.

윈도우 OS 같이 GUI로 파일을 더블클릭 해서 들어가지만, GUI가 없는 리눅스는 디렉토리를 접근할 때마다 절대 경로로 풀 패스를 적어야 합니다.

영타를 잘 못쳐서 경로 오타 나는거 생각하면 끔찍...

만약 상대 경로를 이용하여, 비교 대상이 우리에게 주어지는 상황이라면 위에서 언급한 문제를 해결 할 수 있습니다.

해당 개념을 프로그래밍에 적용해봅시다.

JAVA프로그래밍을 예로 들어 봅시다.

JAVA의 파일 객체를 생성 한다면, 아래와 같이 사용 할 수 있습니다.

public class Main {
private static final String ABSOLUTE_FILE_PATH = "C:\Users\UserID\Desktop\test.txt";
private static final String RELATIVE_FILE_PATH = "test.txt";

   public static void main(String[] args) {
       File absoluteFile = new File(ABSOLUTE_FILE_PATH);
       File relativeFile = new File(RELATIVE_FILE_PATH);
  }
}

첫 번째 파일 객체는 절대 경로를 사용했으므로, 해당 경로의 파일을 참조합니다.

두 번째 파일 객체는 상대 경로를 사용했는데, 어떤 해당 경로의 파일을 참조 할까요?

위에서 설명했드시, 비교 대상 을 생각해야 합니다.

비교 대상은 위에서 작성한 Main.java 파일이 들어있는 디렉토리 기준입니다.

만약 Main.java파일이 C:\Users\UserID\workspace\Test\src 경로에 존재 한다면,

상대 경로test.txt절대 경로 C:\Users\UserID\workspace\Test\src\test.txt로 바꾸어 전달 합니다.

정리하자면, 첫 번째 파일 객체는 C:\Users\UserID\Desktop\test.txt 경로에,

두 번째 파일 객체는 C:\Users\UserID\workspace\Test\src\test.txt 경로를 참조하는 것입니다.

결론적으로

요약하자면, 절대 경로최상위 디렉토리가 반드시 포함 된 경로를 의미하며 상대 경로현재 디렉토리(비교 대상)를 기준으로 작성된 경로를 의미합니다.

설명한 개념을 적용해서 프로그래밍을 한다면, 경로 처리는 완벽하게 할 수 있을거라 생각합니다.

오늘 포스팅은 여기까지 입니다.

읽어주셔서 감사합니다.


'용어정리 > 프로그래밍용어' 카테고리의 다른 글

형상관리의 개념과 이유  (0) 2017.11.01
비즈니스 로직(Business Logic)이란?  (15) 2017.05.24
DTO와 VO란?  (2) 2017.02.08
GET방식 과 POST방식  (32) 2016.12.10
컴포넌트(Component)란?  (8) 2016.10.20

포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

안녕하세요.

오늘은 MongoDB 데이터를 메서드로 주입하는 방법에 대해 포스팅 합니다.

노말한 방법은 MongoDB 데이터를 클래스 필드 이름이랑 매칭 시켜 주입합니다.

노말한 방법은 이전 포스팅을 참고해주세요!

SpringBoot와 MongoDB 연동하기

그런데, 만약 MongoDB데이터의 키 값과 클래스 필드 이름 매칭이 아닌, 클래스 필드 이름은 자유롭게 사용하고 싶을때는 어떻게 해야 할까요? 또는 데이터를 가공해서 넣고 싶을 때는 어떻게 해야 할까요?

즉 이번 포스팅은 MongoDB데이터를 먼저 받고 처리 하는것이 아닌 MongoDB 데이터를 받는 시점에서 처리하는 방법에 관한 것입니다.

@Field Annotation API 사용하기

위에서 언급한데로, 만약 MongoDB데이터의 키 값과 클래스 필드의 이름을 따로 사용하고 싶다면, @Field API를 사용하면 됩니다.

예를들어, MongoDB에 아래와 같은 Document를 가지는 Person Collection이 있다고 가정합니다.

{
   "name": "Mommoo",
   "job": "developer"
}

이 데이터를 주입 받는 Person 클래스를 아래와 같이 만들었다고 가정합니다.

@Document(collection="person")
public class Person {
   private String name;
   private String job;
}

위와 같은 상황이라면, 문제 없이 데이터를 주입 받습니다.

하지만 만약, Person의 멤버변수 namedeveloperName으로 바꾸고 싶다면 아래와 같이 @Filed API를 사용합니다.

@Document(collection="person")
public class Person {
   @Field("name")
   private String developerName;
   private String job;
}

@Field Annotation API를 이용하여 데이터 주입 메서드 만들기

문제상황 설정

Person Collection 정보를 위와 같이 따로 받지 않고, 아래의 NameCard 클래스로 받고 싶다면 어떻게 해야 할까요?

public class NameCard {
   private String developerName;
   priavte String job;
}

당연히 이런건, Person Collection에 NameCard 키를 추가 한 후 거기에 name과 job을 넣어 구조를 맞추는 경우가 많지만...

노말한 방법중 하나는 위의 Person 클래스로 데이터를 받고, NameCard를 뒤늦게 만드는 방법 입니다.

아래의 예시 처럼요.

@Document(collection="person")
@Getter //lombok 라이브러리 getter를 자동으로 만들어 줍니다.
public class Person {
   private String name;
   private String job;
}

public class Person2 {
   private NameCard nameCard;
   
   public Person2(NameCard nameCard){
       this.nameCard = nameCard;
  }
}

//Person을 주입 받은 어떤 메서드가 있다고 가정합니다.
public Person2 convertPersonToPerson2(Person p) {
  NameCard nameCard = new NameCard(p.getName(), p.getJob());
  return new Person2(nameCard);
}

해당 예시의 특징은 persistence class (Person)domain class(Person2)를 나누어서 사용하는 것을 알 수 있습니다. 즉, PersonDTO역할을 하며, Person2business logic역할을 합니다.

하지만, Spring이 추구하는 프로그래밍 패러다임중 하나인 POJO를 이루고자 한다면, 그렇게 좋은 구조는 아닌거 같습니다.

Plain Old Java Object : 그냥, 평범한 객체 오브젝트를 뜻합니다.

객체를 Data Holder로만 사용하면 도메인 영역을 처리하기 어려우니,

기존의 객체지향 방식으로 데이터 와 관련있는 기능들을 클래스에 넣음으로써, 도메인 영역을 더 효율적으로 처리하자는 의미입니다.

POJO방식으로 처리

위에서 언급한 DTObusiness logic을 한곳에서 처리 하기 위해서는 데이터 주입을 원하는 곳으로 받고 처리할 수 있어야 합니다.

아래와 같이 데이터 주입을 메서드로 받음으로써, 처리 할 수 있습니다.

public class Person {
  private NameCard nameCard = new NameCard();
 
  @AccessType(AccessType.Type.PROPERTY)
  @Field("name")
  public setName(String name) {
      nameCard.setDeveloperName(name);
  }
 
  @AccessType(AccessType.Type.PROPERTY)
  @Field("job")
  public setName(String job) {
      nameCard.setJob(job);
  }
}

마무리

생성자로 데이터를 주입받는 방법도 있지만, 해당 방법은 데이터에 대응하는 필드가 반드시 존재 해야 합니다. 그렇기에 위와 같이 필드가 아예 다를 때는 메서드로 주입 받아야 합니다.

주입받는 메서드의 접근 제어자는 무조건 public이어야 합니다. 그렇지 않으면 런타임 에러가 발생합니다.

Jackson 라이브러리를 사용 하신다면, 해당 setter 메서드 때문에 원하지 않게 JSON 구조로 잡힐때가 있습니다. 그럴 경우에는 아래와 같이 @JsonIgnore API를 사용하시면 됩니다.

public class Person {
   
   @JsonIgnore
   @AccessType(AccessType.Type.PROPERTY)
   @Field("name)
   public setName(String name) {
      ...
  }
}


포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee



오늘은 SpringBoot 프레임워크에서 application.properties 파일을 읽는 방법에 대해 포스팅 합니다.

Java 로 파일을 읽을 때, 프로젝트 경로는 보통 아래의 두 API를 통해 읽습니다.

System.getProperty("user.dir") , [class].class.getResource("/").getPath()

직접 구현해보시면 아시겠지만, 많은 이유로 프로젝트 경로를 구하는 것은 생각보다 까다롭습니다.

예를들어, 멀티 모듈 프로젝트 또는 resource 폴더 위치에 따른 변동사항 등등 문제로 골치아픕니다.

하지만, SpringBoot@Value API 또는 Environment API 를 사용하면 편리합니다.

@Value API

클래스 변수위에 어노테이션으로 설정 할 수 있는것이 특징입니다.

간단하게 @Value(${프로퍼티 키})로 선언하면 됩니다.

예를들어, 웹 프로젝트의 컨텍스트 루트 경로를 코드상에서 알고 싶을때 아래와 같이 코드를 작성하면 됩니다.

public class ProjectContext {
   @Value(${"server.servlet.context-path"})
   private String contextPath;
   
   public String getContextPath() {
       return contextPath;
  }
}

문제는 Spring 생명주기 순서 문제로, null 값이 뜰 때가 있습니다.

만약 해당 문제점이 있다면, 아래에서 소개할 Environment API를 사용해야 합니다.

Environment API

Environment APIgetProperty(프로퍼티 키) 메서드를 사용합니다.

마치 JavaProperties API의 사용법과 비슷합니다.

마찬가지로 컨텍스트 루트 경로를 구한다고 가정해봅시다.

public class ProjectContext {
  private static final String CONTEXT_PATH_KEY = "server.servlet.context-path";
  private Environment environment;

  @Autowired
  public ProjectContext(Environment environment) {
      this.environment = environment;
      System.out.println(getContextPath()); // /Context-Path 출력
}

  public String getContextPath() {
      return environment.getProperty(CONTEXT_PATH_KEY);
}
}

Environment API@Value API 와 달리, Environment 객체가 안전하게 주입 되면,

안전하게 application.properties 을 읽을 수 있습니다.



포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee



안녕하세요 오늘 포스팅은 SpringBoot와 MongoDB를 연동 방법입니다.


저는 최소한의 설정만을 원칙으로 하기 때문에, 연동에 필요한 부분만 알려드리고자 합니다.


SpringBoot 프레임워크가 이미 구현 해 놓은 API가 있어서 정말 간단합니다.


주의!!

해당 포스팅은 MongoDB Server 다운로드, MongoDB 개념, 사용법 등을 설명하지 않습니다.



SpringBoot 환경과 MongoDB 환경 정보


SpringBoot는 패키지 매니저로 Gradle(4.8.1)을 사용하구요, 

SpringBoot 프레임워크 버전은 2.1.0.RELEASE 입니다.

MongoDB는 윈도우10 x64 전용으로 4.0.4 버전입니다.



SpringBoot 설정


먼저 SpringBoot 프로젝트에 필요한 의존 라이브러리를 build.gradle 파일의 dependencies에 아래와 같이 추가합니다.


버전을 따로 기입 안함으로써, 2.1.0.RELEASE 버전이 제시하는 Default 버전의 라이브러리가 의존됩니다.


dependencies {

...
implementation('org.springframework.boot:spring-boot-starter-data-mongodb')
...
}


그다음, application.properties 파일에 MongoDB 정보를 아래와 같이 기입해야 합니다.


application.properties 파일은 보통 프로젝트/src/main/resource 경로에 있습니다.


spring.data.mongodb.uri=mongodb://[ip 정보]:[port 정보]
spring.data.mongodb.database=[데이터베이스 이름]


몽고 DB의 기본 정보는 localhost(127.0.0.1) ip와 27017 포트 구성을 가집니다.


MongoDB Server에 Database를 만드셨다면, 몽고 DB의 URI 예시는 다음과 같습니다.


mongodb://127.0.0.1:27017/MyDatabase


저는 데이터베이스 이름을 따로 설정했는데요, 아래와 같이 uri 정보에 한번에 기입할 수도 있습니다.


spring.data.mongodb.uri=mongodb://[ip 정보]:[port 정보]/[데이터베이스 이름]


해당 정보는 자바 파일로도 작성 가능합니다.

@Configuration
public class MongoDBConfiguration extends AbstractMongoConfiguration {
@Override
public MongoClient mongoClient() {
return new MongoClient("ip 정보", 포트정보);
}

@Override
protected String getDatabaseName() {
return "데이터베이스 이름";
}
}


MongoDB에 Document에 대응하는 DTO 클래스 만들기.


MongoDB Document가 아래와 같은 구조라 가정한다면,


Person Document


1
2
3
4
{
    "name""Mommoo",
    "job""Developer"
}
cs


DTO Class는 다음과 같이 작성됩니다.


Person.java


1
2
3
4
5
6
7
8
9
public class Person {
    private String name;
    private String job;
 
    @Override
    public String toString() {
        return "name is " + name + " job is " + job;
    }
}
cs


주의!!


보통은 Document 어노테이션을 통해, class와 매치되는 Collection을 맵핑 해주어야 합니다.

ex) @Document(collection="myCollection")


Document에 들어있는 모든 필드를 기입했다면, Document 어노테이션을 적지 않아도 됩니다만,

만약 필요한 필드만 기입하면 Document 어노테이션을 반드시 기술 해주어야 합니다.


DTO 클래스에 DB 데이터를 넣어 줄 Repository 객체 작성하기.


정말 간단하게, 아래와 같이 코딩하면 됩니다. 


특징으로는, MongoRepository 제네릭 부분에 앞서 작성한 DTO Class를 입력하면 됩니다. ( 예시로는 Person )


PersonMongoDBRepository.java


1
2
3
public interface PersonMongoDBRepository extends MongoRepository<Person, String> {
 
}
cs


몇몇 분들은 JPA 방식이라는 것을 눈치채셨을 겁니다. 


MongoDB의 Document 데이터를 호출하는 메서드는 기본적으로 MongoRepository에 이미 기술 되어 있습니다.


MongoDB의 쿼리랑 대응되는 메서드가 기본으로 존재합니다.

(findAll, insert 등등)


SQL문에 Where 조건절에 대응되는 쿼리를 작성하고 싶다면, MongoDB의 Query대로 method를 작성하면 됩니다.

예를들어, name 필드값 기준인 Document만 호출하고 싶다면 아래와 같이 작성됩니다.


findBy + 필드이름


1
2
3
public interface PersonMongoDBRepository extends MongoRepository<Person, String> {
    public Person findByName(String name);
}
cs


더 자세한 쿼리 예시를 보고 싶다면 아래의 사이트의 6.3 Query Methods 부분을 참고해주세요.

https://docs.spring.io/spring-data/mongodb/docs/1.2.0.RELEASE/reference/html/mongo.repositories.html



테스트 해보기.


아래와 같이 테스트 코드를 작성해서 호출한 데이터가 출력되는지 확인합니다.


SpringBootMongoDBTest.java


1
2
3
4
5
6
7
8
9
10
11
@RunWith(SpringRunner.class)
@SpringBootTest()
public class SpringBootMongoDBTest {
    @Autowired()
    private ProjectMongoDBRepository projectMongoDBRepository;
 
    @Test
    public void printProjectData() {
        System.out.println(projectMongoDBRepository.findAll());
    }
}
cs


저는 프로젝트/src/test/java/패키지 안에다가 테스트 class를 만들어 실행했습니다.



지금까지, MongoDB 연동 방법에 대해 알아봤습니다.


제가 소개한 방법은 MongoDB의 특징인 Query 조회와 비슷하게 접근 할 수 있는 방법입니다.

(JPA 방법이죠)


기존 MySQL와 같은 관계형 데이터베이스 방법대로 SQL문으로 조회하는 방법도 있습니다.

구글에 mongoDB template로 검색해보세요.


읽어주셔서 감사합니다.


포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

+ Recent posts