반응형


인텔리제이에서 gradle프로젝트를 import해서 build하는데 자꾸 에러메세지 발생 및 자바코드를 프로젝트내에서 정상적으로 


소스코드로 인식하지 못하는 문제가 있었다.


일반적으로는 intellij의 gradle플러그인을 통해 기본적으로 프로젝트의 소스코드 path를 잡아주어야 하지만 소스코드도 잡지 못하고 


gradle dependency도 정상적으로 로드하지 못해 소스코드도 다 에러가 발생하는 상황.,,..


 이런식으로 소스코드를 인식하지 못한경우 주황색(일반텍스트)파일로 인식한다. 


정상적으로 인식을 했다면   이렇게 파란색으로 클래스파일로 인식을 하여야 정상.


문제의 원인은 intellij 2018.01 버전을 사용하고 있었는데 해당 버전과 gradle최신 버전 사용시 버그가 존재했던 것 같다.


intellij의 버전을 2018.3으로 업데이트 해주니 정상적으로 소스코드도 path도 잘찾아 인식하고 dependency도 잘불러오는 것을 확인했다.......


intellij 버전에 관련된 첫 삽질이라....기록 남겨본다.


정상적으로 intellij에서 gradle프로젝트로 import했다면 gradle창에서 다음과 같이 'Source Sets, Tasks, Run configurations' 디렉토리를 확인할 수 있다.


업데이트 전에는 manager.ectr이외에 그 하위 디렉토리가 보이지 않았다.....


혹시나 intellij(인텔리제이) 사용시 gradle 프로젝트가 정상적으로 import되지 않는다면 inteelij 버전에 맞는 gradle의 버전을 사용하고 있는지 


intellij의 버전이 너무 낮지 않은지 확인해보길 바란다.




반응형
반응형


자바 프로젝트 내부에서 jdbc드라이버를 연결하는 도중 다음과 같은 에러가 발생하였다.


detailMessage = "Could not create connection to database server. Attempted reconnect 3 times. Giving up."

cause = com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'KST' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.



[ 소스코드 ] 

DriverManger.getConnection하는 과정에서 발생

final private static String url = "jdbc:mysql://10.111.eee.xxx:5555/db?characterEncoding=utf8";
final private static String usr = "vuser";
final private static String pwd = "pwd11111";

Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, usr, pwd);
Statement stmt = conn.createStatement();


[ 문제해결 ] 

mysql-connector-java 버전 5.1.X 이후 버전부터 KST타임존을 인식하지 못하는 이슈가 있다고 한다.

따라서 url에 serverTimezone을 추가해준다!

"&serverTimezone=UTC"

String url = "jdbc:mysql://10.111.eee.xxx:5555/adflat?characterEncoding=utf8&serverTimezone=UTC";


이렇게 하면 손쉽게 해결할 수 있다~아니면 mysql-connector-java버전을 낮추거나~

반응형
반응형


최근에 특정 모듈을 분석해서 다시 만들어야 하는 작업을 진행하였다.


해당 모듈이 약간 성능에 민감한 모듈이다 보니 가능한 한 빠르게 동작하도록 하였어야 했다.


그러다가 ArrayList.contains() 메서드로 된 부분이 있어 HashSet.continas으로 수정해 성능이 조금 높아 진 것을 확인했는데


기본적인 개념으로만 HashSet이 내부적으로 HashMap을 구현하고 있어서 빠를 것 같다는 생각을 했었는데

좀 더 자세히 알고 싶어 포스팅 하게 되었다.


[ HashSet.Contains() ]

내부적으로 HashSet은 HashMap Instance를 구현하고 있고 contains() 메서드를 호출하게되면 HashMap.containsKey(object)가 호출된다고 보면 된다. 자세한 설명은 밑을 참고하자.

Internally, the HashSet implementation is based on a HashMap instance. The contains() method calls HashMap.containsKey(object).

Here, it’s checking whether the object is in the internal map or not. The internal map stores data inside of the Nodes, known as buckets. Each bucket corresponds to a hash code generated with hashCode() method. So contains() is actually using hashCode() method to find the object’s location.

Now let’s determine the lookup time complexity. Before moving ahead, make sure you are familiar with Big-O notation.

On average, the contains() of HashSet runs in O(1) time. Getting the object’s bucket location is a constant time operation. Taking into account possible collisions, the lookup time may rise to log(n) because the internal bucket structure is a TreeMap.

This is an improvement from Java 7 which used a LinkedList for the internal bucket structure. In general, hash code collisions are rare. So we can consider the elements lookup complexity as O(1).



[ ArrayList.contains() ] 

ArrayList는 해당 값이 list에 있는지 판단하기 위해 내부적으로 indexOf(object) 메서드를 사용한다. indexOf(object) 메서드는 array 전체를 반복해서 돌고 각각의 element와 비교를 진행한다. 자세한 설명은 밑을 참고하자.

Internally, ArrayList uses the indexOf(object) method to check if the object is in the list. The indexOf(object)method iterates the entire array and compares each element with the equals(object) method.

Getting back to complexity analysis, the ArrayList.contains() method requires O(n) time. So the time we spend to find a specific object here depends on the number of items we have in the array. 



설명을 봤듯이 HashSet은 내부적으로 HashMap을 사용하고 ArrayList는 contains 메서드 내부에서 모든 element들을 돌며 비교작업을 진행하기 때문에 당연히 HashSet의 contains메서드가 빠를 수 밖에 없었던 것이다.


[ 테스트 코드 ]

1. 100만개의 String을 생성하여 HashSet, ArrayList를 만든다.

2. 500번 반복문을 돌며 Set과 List의 contains메서드를 사용해 확인한다.


HashSet Test

@Test
public void testHashSetContainsPerformance() {
Set<String> testSet = new HashSet<>();
String baseStr = "hellotheworld";
Random generator = new Random();

long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
testSet.add(baseStr + i);
}
long end = System.currentTimeMillis();
System.out.println("Setup Performance : " + (end - start));

long start2 = System.currentTimeMillis();
int matchingCount = 0;
for (int j =0; j < 500; j++) {
if (testSet.contains(baseStr + generator.nextInt(5000))) {
matchingCount++;
}
}
long end2 = System.currentTimeMillis();
System.out.println("HashSet Contains Performance : " + (end2 - start2));
}


ArrayList Test

@Test
public void testArrayListContainsPerformance() {
List<String> testList = new ArrayList<>();
String baseStr = "hellotheworld";
Random generator = new Random();

long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
testList.add(baseStr + i);
}
long end = System.currentTimeMillis();
System.out.println("Setup Performance : " + (end - start));

long start2 = System.currentTimeMillis();
int matchingCount = 0;
for (int j =0; j < 500; j++) {
if (testList.contains(baseStr + generator.nextInt(5000))) {
matchingCount++;
}
}
long end2 = System.currentTimeMillis();
System.out.println("ArrayList Contains Performance : " + (end2 - start2));
}


[ 결과 (시간단위 : ns) ]


결과를 보게되면 실제 set, list에 데이터를 쌓을 때는 hashset이 약 2배 정도 오래 걸리는 것을 알 수 있다. 

(아마 내부적으로 해쉬함수를 돌려 넣기 때문일 것으로 추정)

하지만 contains메서드의 hashset은 거의 시간이 안걸린다고 볼 수 있는 반면에 list는 set에 비해서는 꽤나 걸리는 것으로 볼 수 있다. 

따라서 한 번 데이터를 초기화해놓고 get이나 contains메서드를 사용할 일이 많은 시스템이라면 list보다는 set으로 자료구조를 가져가는 편이 좋다고 할 수 있을 것 같다.


포스팅을 마치도록 하겠습니다. 감사합니다.

도움이 되셨다면 광고 한 번 클릭해주시는 센스^_^


참고 : https://www.baeldung.com/java-hashset-arraylist-contains-performance



반응형
반응형

스프링부트(SpringBoot)로 카프카에서 데이터를 consume에 처리해야 하는 일이 있어 작업 중 발생한 에러에 대해 간단히 남겨본다...

나중에 같은 실수를 하지 않기 위해...


일단 현재 사용하고 있는 카프카 버전은 0.8.2.0이고 spring-integration-kafka를 사용하였다.

실제 클라우데라에 설치된 카프카 버전


카프카 낮은 버전을 사용하고 있어 요즘 spring-kafka 연동 가이드로는 에러가 발생해 삽질을 좀 하였다...

시간이 된다면 0.8.2.2 consume 모듈을 git에 올려서 링크 걸어보도록 하겠다.


다음과 같이 KafkaConfig를 설정해주고


[ 스프링 설정 및 코드 ]

@EnableIntegration
@Configuration
public class KafkaIntegration {

private static final String BOOTSTRAP_SERVER = "serverIp:2181";
private static final String ZOOKEEPER_CONNECT = "serverIp:2181";

@Getter
@Component
public static class KafkaConfig {
private String topic = "data_log";
private String brokerAddress = BOOTSTRAP_SERVER;
private String zookeeperAddress = ZOOKEEPER_CONNECT;

KafkaConfig(){}

public KafkaConfig(String t, String b, String zk) {
this.topic = t;
this.brokerAddress = b;
this.zookeeperAddress = zk;
}
}
}


Consumer 빈 등록

@Configuration
public class ConsumerConfiguration {

@Autowired
private KafkaIntegration.KafkaConfig kafkaConfig;

@Bean
public IntegrationFlow consumer() {

KafkaHighLevelConsumerMessageSourceSpec messageSourceSpec = Kafka.inboundChannelAdapter(
new ZookeeperConnect(this.kafkaConfig.getZookeeperAddress()))
.consumerProperties(props -> props.put("auto.offset.reset", "smallest")
.put("auto.commit.interval.ms", "100"))
.addConsumer("ectc_dsp_test", metadata -> metadata.consumerTimeout(100)
.topicStreamMap(m -> m.put(this.kafkaConfig.getTopic(), 1))
.maxMessages(10).valueDecoder(String::new));

Consumer<SourcePollingChannelAdapterSpec> endpointConfigurer =
e -> e.poller(p -> p.fixedDelay(100));

return IntegrationFlows
.from(messageSourceSpec, endpointConfigurer)
.<Map<String, List<String>>>handle((payload, headers) -> {
payload.entrySet().forEach(
e -> System.out.println((e.getKey() + '=' + e.getValue())));
return null;
})
.get();
}

}


으로 등록 후 어플리케이션을 실행하면 다음과 같은 에러메시지가 발생하였다.


[ 발생한 에러메세지 ]

kafka.common.KafkaException: fetching topic metadata for topics [Set(dsp_log)] from broker [ArrayBuffer(id:245,host:eedkaf-dmp001.svr.net,port:9092, id:94,host:eedkaf-dmp002.svr.net,port:9092, id:95,host:eedkaf-dmp003.svr.net,port:9092)] failed

at kafka.client.ClientUtils$.fetchTopicMetadata(ClientUtils.scala:72) ~[kafka_2.11-0.8.2.0.jar:na]

at kafka.client.ClientUtils$.fetchTopicMetadata(ClientUtils.scala:93) ~[kafka_2.11-0.8.2.0.jar:na]

at kafka.consumer.ConsumerFetcherManager$LeaderFinderThread.doWork(ConsumerFetcherManager.scala:66) ~[kafka_2.11-0.8.2.0.jar:na]

at kafka.utils.ShutdownableThread.run(ShutdownableThread.scala:60) [kafka_2.11-0.8.2.0.jar:na]

Caused by: java.nio.channels.ClosedChannelException: null

at kafka.network.BlockingChannel.send(BlockingChannel.scala:100) ~[kafka_2.11-0.8.2.0.jar:na]

at kafka.producer.SyncProducer.liftedTree1$1(SyncProducer.scala:73) ~[kafka_2.11-0.8.2.0.jar:na]

at kafka.producer.SyncProducer.kafka$producer$SyncProducer$$doSend(SyncProducer.scala:72) ~[kafka_2.11-0.8.2.0.jar:na]

at kafka.producer.SyncProducer.send(SyncProducer.scala:113) ~[kafka_2.11-0.8.2.0.jar:na]

at kafka.client.ClientUtils$.fetchTopicMetadata(ClientUtils.scala:58) ~[kafka_2.11-0.8.2.0.jar:na]

... 3 common frames omitted


분명 로그를 봐도 내가 KafkaConfig에서 설정해준 서버 IP와 정상적인 커넥션을 맺었는데 왜이런단 말인가???

2018-11-20 12:12:05.668  INFO 87935 --- [161.26.70:2181)] org.apache.zookeeper.ClientCnxn          : Session establishment complete on server 내가지정한서버IP:2181, sessionid = 0x366e7594a3918e9


[ 해 결 ]

주키퍼(Zookeeper) 내부적으로 클러스터간 통신시 혹은 zookeeper to kafka간 통신시 서버의 IP보다는 호스트명으로 서로를 인지한다는 말?을 들은적이 있어 로컬 host파일에 서버의 호스트와 IP를  등록하고 다시 실행해보았더니 정상적으로 카프카에서 메세지를 consume하는 것을 확인할 수 있었다.


혹시나 다음과 같은 문제가 발생한다면 host파일에 서버의 호스트명과 IP를 등록 후 다시 해보길...


정확한 원인은 나중에 관련한 문서나 Zookeeper를 좀더 깊게 공부하게 되어 발견하게 된다면 추후 또 포스팅해보도록 하겠습니다. 


ref : https://spring.io/blog/2015/04/15/using-apache-kafka-for-integration-and-data-processing-pipelines-with-spring

반응형
반응형

일반적인 map.size() 말고 실제 메모리에 올라가는 map byte사이즈를 알고 싶은경우~


다음과 같이 사용하면 될듯


public static void size(Map map) {
try {
System.out.println("Index Size: " + map.size());
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(map);
oos.close();
System.out.println("Data Size: " + baos.size());
} catch(IOException e){
e.printStackTrace();
}
}


baos.size() 메소드를 들어가보면 다음과 같이 bytes를 리턴한다.

/**
* Returns the current size of the buffer.
*
* @return the value of the <code>count</code> field, which is the number
* of valid bytes in this output stream.
* @see java.io.ByteArrayOutputStream#count
*/
public synchronized int size() {
return count;
}


확인할일이 있어서 보다가 정리

반응형
반응형

자바 Stream에서 Map, Object내부에 또 다른 Map, Set, List와 같은 Collection이 있을 때 각 value를 개별된 값으로 처리하고 싶은 경우



Student POJO

public class Student {

private String name;
private Set<String> book;

public void addBook(String book) {
if (this.book == null) {
this.book = new HashSet<>();
}
this.book.add(book);
}
//getters and setters

}



flatMap() and Set example

1차적으로 map으로 POJO에 Set에 담긴 Book데이터를 가져와 flatmap으로 펼친 후 list로 collect

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class TestExample2 {

public static void main(String[] args) {

LexiconMain.Student obj1 = new LexiconMain.Student();
obj1.setName("mkyong");
obj1.addBook("Java 8 in Action");
obj1.addBook("Spring Boot in Action");
obj1.addBook("Effective Java (2nd Edition)");

LexiconMain.Student obj2 = new LexiconMain.Student();
obj2.setName("zilap");
obj2.addBook("Learning Python, 5th Edition");
obj2.addBook("Effective Java (2nd Edition)");

List<LexiconMain.Student> list = new ArrayList<>();
list.add(obj1);
list.add(obj2);

List<String> collect =
list.stream()
.map(x -> x.getBook()) //Stream<Set<String>>
.flatMap(x -> x.stream()) //Stream<String>
.distinct()
.collect(Collectors.toList());

collect.forEach(x -> System.out.println(x));
}

}


Output

Spring Boot in Action
Effective Java (2nd Edition)
Java 8 in Action
Learning Python, 5th Edition


Stream관련 자세한 예제가 더 궁금하다면 아래 링크로


참고 : https://www.mkyong.com/java8/java-8-flatmap-example/

반응형
반응형


MAC 재시작 했을 때


자꾸 전에 작업하다가 자동 저장 된 파일이 떠서 귀찮은 경우가 있다.


이때 불필요하게 자동저장된 데이터라면 다음 경로로 가서 삭제해주도록 한다.


PowerPower이외에도 Microsoft제품들의 자동저장된 파일의 경로이다.


Finder "/Users/< username >/Library/Containers/com.microsoft.*


해당 경로 "/Users/< username >/Library/Containers/"로 가보면 다음의 microsoft폴더들을 볼 수 있다.


Powerpoint의 경우 

"/Users/< username >/Library/Containers/com.microsoft.Powerpoint/Data/Library/Preferences/AutoRecovery"


에 가보면 지긋지긋하게 귀찮게 굴었던 자동저장된 파일들이 있는 것을 볼 수 있다. (_autorecover 파일)


시원하게 날려주자~안녕~



반응형
반응형

[ Java8 ] Stream Collectors toMap 사용시 Duplicate Key Error


DB로부터 데이터를 가져와 List에 담고 List에 담긴 데이터를 MAP에 담아 처리하는 작업이다.


List to Map 시 Key 값이 중복일 경우 Duplicate Key에러 발생

2018:08:08 19:47:49.947 ERROR --- [http-nio-80-exec-24] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.util.concurrent.CompletionException: java.lang.IllegalStateException: Duplicate key 31356962-6f5b-4280-9beb-f48d4c437695] with root cause

java.lang.IllegalStateException: Duplicate key 31356962-6f5b-4280-9beb-f48d4c437695

        at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)

        at java.util.HashMap.merge(HashMap.java:1254)



[ 에러가 발생했던 코드 ]

CompletableFuture<List<Partner>> matchedList = cookieMatchingRepository.getMatchedPartnerUserIdByBid(bid);
Map<String, String> matchedPartnerMap =
matchedList.get().stream().collect(Collectors.toMap(Partner::getPartnerId, Partner::getPartnerUserId));


[ 동일한 KEY값이 들어와도 처음 KEY-VALUE 값으로 처리하는 코드 (노란 부분이 바뀜) ]

CompletableFuture<List<Partner>> matchedList = cookieMatchingRepository.getMatchedPartnerUserIdByBid(bid);
Map<String, String> matchedPartnerMap = matchedList.get().stream()
.collect(Collectors.toMap(Partner::getPartnerId, Partner::getPartnerUserId, (p1, p2) -> p1));



반응형
반응형

[ Javascript ] 자바스크립트 ajax 크로스도메인 이슈(CORS)

특정 서버의 API를 테스트해야하는 페이지를 작업중 크로스도메인 이슈에 맞닥뜨리게 되었다.
예전 고객센터시스템 개발할 때 자주접하던 이슈였기에 쉽게 해결할 수 있을 줄 알았으나....꽤나 삽질을 많이 했다.
그래서 겸사겸사 정리하며 다시 한 번 머리에 각인!

1. 크로스도메인이슈 (CORS, Cross Origin Resources Sharing)

jQuery 1.5.0이후 버전부터는 이러한 crossdomain을 원천 봉쇄해버렸다.
테스트페이지(웹) -> 쿠키매칭시스템API호출
 http://alpha-adserver.toast.com/ -> http://cm-exchange.toast.com

크로스 도메인은 서로 다른 도메인 간 호출로 위와 같이 호출하게되면 크로스도메인이슈가 발생하게 된다.
보통 서브도메인만 다른 경우의 사이트끼리의 정보 교환은 document.domain만 맞추어 주면 해결이 가능하다.
ex)
1. abc.toast.com
2. def.toast.com
사이트끼리의 정보 교환은 해당페이지 스크립트에 document.domain='toast.com' 설정시 해결가능

하지만 이번 경우에는 브라우져 -> 서버간의 호출로 다음과 같은 문제로 해결이 힘든 상황.
따라서 javascript, ajax로 요청시 다음과 같은 에러가 발생
스크린샷 2018-07-25 오후 5.16.21.png
이렇게 외부로 요청이 안되는 것은 자바스크립트 엔진 표준 스펙의 동일 출처 정책(SOP, Same-Origin Policy)이라는 보안 규칙이 있기 때문


2. 동일 출처 정책 (SOP, Same-Origin Policy)

웹어플리케이션 보안 모델에서 중요한 개념중 하나
해당 정책으로 인해 자바스크립트(XMLHttpRequest)로 다른 웹페이지 접근시 같은 출처(same origin)의 페이지에만 접근 가능하다.
같은 출처라는 것은 Protocol, Host명, Port가 같다는 것을 의미
쉽게 말해, 웹페이지의 스크립트는 그 페이지와 같은 서버에 있는 주소로만 ajax요청을 할 수 있다는 것이다.
스크린샷 2018-07-25 오후 5.19.29.png


3. CORS 작동 방식

스크린샷 2018-07-25 오후 5.21.56.png
Preflight request(사전요청) - 요청하려는 URL이 외부 도메인일 경우 웹 브라우저는 preflight요청을 먼저 날린다.
preflight요청은 실제로 요청하려는 경로와 같은 URL에 대해 OPTIONS 메서드로 요청을 미리 날려보고 요청 할 수 있는 권한이 있는지 확인한다.
CORS요청을 편법없이 처리하기 위해서는 클라이언트 처리만으로는 어렵고 서버측에서 preflight요청을 처리하는 기능이 필요하다.


4. CORS 해결할 수 있는 방법.

1) 웹 브라우저 실행옵션이나 플러그인인을 통한 동일출처 정책 회피(크롬 --disable-web-security옵션을 추가하여 실행)

-> 테스트하는 컴퓨터마다 설정해야하는 번거로움으로 X

2) jsonp(json with padding)를 통한 해결

JSONP는 HTML의 script 요소로부터 요청되는 호출에는 보안상 정책이 적용되지 않는다는 점을 이용한 우회 방법
다시 한번 확실히 하자면, script 요소는 src를 호출한 결과를 javascript를 불러와서 포함시키는 것이 아니고 실행시키는 태그입니다.

다음과 같이 dataType만 변경해서 보내면된다??? nono 서버에서 수정해줘야 함...(일반적인 웹사이트에서는 dataType만 jsonp로 고치면 된다는 식으로 말하고있음...)

$.ajax({
    type: 'GET',
    url: url,
    dataType: 'jsonp',   //cross-domain 이슈를 회피하기 위해 jsonp요청을 한다.
    success: callback    //callback이란 함수를 나의 success function에 매핑시켜 콜백요청을 처리할 것이다.
});

or

$.getJSON(url + "?callback=?", data, callback);</span>

RESPONSE의 경우
callback({'kingbbode', age:28})

이처럼 Callback 함수명으로 감싸져서 오는데 서버에서 위와같이 감싸진 형태의 Text로 내려주어야한다!!!!
Text로 내려진 데이터가 Javascript로 실행되는 과정을 Jquery에서 지원하여 Success 함수로 연결시켜 주는 것이다.

정확한 서버구현과 내용은 참고 : jsonp참고페이지

**-> 서버를 건드릴거면 Accss-Control-allow-origin만 설정해주는 것이 훨씬 간편... **

3) 중간에 proxy서버를 두는 방법

-> 배보다 배꼽이 더큰 상황...

4) jquery.ajaxPrefilter()

ajax요청전 내부적으로 $.ajaxPrefilter()를 거친다. 따라서 ajaxPrefilter()내부를 변조하면 cross-domain문제를 해결할 수 있다. 라는 느낌?
밑의 코드는 통신 자체를 'jsonp'로 속여 보내는 방식으로 처리가능하다고 하는데 나의 경우는 정상동작 안함...(http://igna.tistory.com/20?category=430584)

$.ajaxPrefilter('json', function(options, orig, jqXHR) {
    if (options.crossDomain && !$.support.cors) return 'jsonp'
});

$.ajax({
    type: 'GET',
    crossDomain: true,
    url: url,
    dataType: 'json',
    success: function(data, textStatus, xhr) {        $('#bidValue').val(JSON.stringify(data));
}});

5) 서버에서 Accss-Control-allow-origin 설정을 통한 요청 허용

API컨트롤러에 다음과 같은 애노테이션 선언
특정 도메인만 허용하고 싶은 경우에는 별도 config로 설정 후 사용
참고 : 스프링에서 cors설정

@CrossOrigin("*")

>> 결국에는 서버에 Accss-Control-allow-origin으로 특정 도메인을 풀어주는 것으로 해결

ref :  http://enterkey.tistory.com/409


반응형

+ Recent posts