어제 정상조회되던 서비스에 오류가 났다.

Error attempting to apply AttributeConverter

{"MAASLOG"="[2022-10-24 15:08:21][ERROR][http-nio-8082-exec-8][BatchErrorCollectService.java:65][inquiryList:Error attempting to apply AttributeConverter]"} javax.persistence.PersistenceException: Error attempting to apply AttributeConverter at org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter$2.doConversion(AttributeConverterSqlTypeDescriptorAdapter.java:148) at org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter$2.extract(AttributeConverterSqlTypeDescriptorAdapter.java:121) at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:257) at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:253) at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:243) at org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:329) at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:3130) at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1869) at org.hibernate.loader.Loader.hydrateEntityState(Loader.java:1797) at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1770) at org.hibernate.loader.Loader.getRow(Loader.java:1622) at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:740) at org.hibernate.loader.Loader.getRowsFromResultSet(Loader.java:1039) at org.hibernate.loader.Loader.processResultSet(Loader.java:990) at org.hibernate.loader.Loader.doQuery(Loader.java:959) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:349) at org.hibernate.loader.Loader.doList(Loader.java:2849) at org.hibernate.loader.Loader.doList(Loader.java:2831) at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2663) at org.hibernate.loader.Loader.list(Loader.java:2658) at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:506) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:400) at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:219) at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1414) at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1625) at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1593) at org.hibernate.query.Query.getResultList(Query.java:165) at com.querydsl.jpa.impl.AbstractJPAQuery.getResultList(AbstractJPAQuery.java:160) at com.querydsl.jpa.impl.AbstractJPAQuery.fetch(AbstractJPAQuery.java:202) at com.sejong.mwserver.service.admin.BatchErrorCollectService.inquiryList(BatchErrorCollectService.java:52) at com.sejong.mwserver.service.admin.BatchErrorCollectService$$FastClassBySpringCGLIB$$c7f78cc1.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) at com.sejong.mwserver.service.admin.BatchErrorCollectService$$EnhancerBySpringCGLIB$$2d72030a.inquiryList(<generated>) at com.sejong.mwserver.controller.admin.BatchErrorCollectController.inquiryList(BatchErrorCollectController.java:51) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:113) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at com.sejong.mwserver.security.JwtRequestFilter.doFilterInternal(JwtRequestFilter.java:66) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at argo.server.valves.LenaStuckThreadDetectionValve.invoke(LenaStuckThreadDetectionValve.java:226) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.IllegalArgumentException: Unsupported type for 04 at com.sejong.mwserver.entity.code.AbstractBaseEnumConverter.lambda$1(AbstractBaseEnumConverter.java:32) at java.util.Optional.orElseThrow(Optional.java:290) at com.sejong.mwserver.entity.code.AbstractBaseEnumConverter.convertToEntityAttribute(AbstractBaseEnumConverter.java:32) at com.sejong.mwserver.entity.code.AbstractBaseEnumConverter.convertToEntityAttribute(AbstractBaseEnumConverter.java:1) at org.hibernate.metamodel.model.convert.internal.JpaAttributeConverterImpl.toDomainValue(JpaAttributeConverterImpl.java:45) at org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter$2.doConversion(AttributeConverterSqlTypeDescriptorAdapter.java:140) ... 138 common frames omitted

아래와 같이 convertor 를 적용하고 있었다.

아마 enum에 정의되지 않은 값이 DB에 등록되어 발생한 것 같다.

@Getter
@AllArgsConstructor
//@EnumCodeAnnotation(name = "AFFI_CLSS_CD")
public enum MistkProsStatCd implements BaseEnumCode<String> {

	오류발생("01"),
	오류아님("10"),
	조치중("20"),
	조치완료("30"),
	보류("40")
	;
	
	private final String value;
	
	private String value() {
		return value;
	}
	
	private static final Map<String, MistkProsStatCd> BY_VALUE =
            Stream.of(values()).collect(Collectors.toMap(MistkProsStatCd::value, e -> e));
	
	public static MistkProsStatCd getEnumByValue(String value) {
		return BY_VALUE.get(value);
	}
}

@Converter(autoApply = true)
public class MistkActnStatCdConverter extends AbstractBaseEnumConverter<MistkActnStatCd, String>{

	@Override
	protected MistkActnStatCd[] getValueList() {
		return MistkActnStatCd.values();
	}

}

오류 부위는 아래의 IllegalArgumentException throw로 발생했다

public abstract class AbstractBaseEnumConverter<X extends Enum<X> & BaseEnumCode<Y>, Y> 
														implements AttributeConverter<X, Y>{

	protected abstract X[] getValueList();
	
	@Override
	public Y convertToDatabaseColumn(X attribute) {
		
		if(attribute == null) {
			return null;
		}
		
		return attribute.getValue();
	}
	
	@Override
	public X convertToEntityAttribute(Y dbData) {
		
		if(dbData == null) {
			return null;
		}
		
		return Arrays.stream(getValueList())
					 .filter(e -> e.getValue().equals(dbData))
					 .findFirst()
					 .orElseThrow(() -> new IllegalArgumentException(String.format("Unsupported type for %s", dbData)))
					 ;
	}
}

DB에 04가 있는데 매핑을 뭐로 하라고!!! 
미안하다. DB 내가 안넣었다. 확인해볼께.

'tools' 카테고리의 다른 글

ecilpse 속도 개선  (1) 2023.09.12
vsCode git pull request  (0) 2023.04.16
git. push가 갑자기 안되네.. github가 잘못했네.  (0) 2021.10.15
VSCode Workspace Trust?  (0) 2021.08.27
Visual Studio Code  (0) 2021.06.20

화면에서 넘어오는 객체 매핑을 못하는것 같다.

화면의 기간날짜 폼 데이터를 문자열로 받아오는 과정에서 setter를 재정의 하여 

시작일자, 종료일자 정보에 매핑하여 처리하려 했는데, 오류가 발생했다.

{"MAASLOG"="[2022-10-22 23:52:04][WARN ][http-nio-8082-exec-10][AbstractHandlerExceptionResolver.java:207][logException:Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'batchErrorCollectFormDto' on field 'endDttm': rejected value [null]; codes [typeMismatch.batchErrorCollectFormDto.endDttm,typeMismatch.endDttm,typeMismatch.java.lang.Long,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [batchErrorCollectFormDto.endDttm,endDttm]; arguments []; default message [endDttm]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Long' for property 'endDttm'; nested exception is java.lang.NumberFormatException: For input string: "null"]
Field error in object 'batchErrorCollectFormDto' on field 'startDttm': rejected value [null]; codes [typeMismatch.batchErrorCollectFormDto.startDttm,typeMismatch.startDttm,typeMismatch.java.lang.Long,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [batchErrorCollectFormDto.startDttm,startDttm]; arguments []; default message [startDttm]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Long' for property 'startDttm'; nested exception is java.lang.NumberFormatException: For input string: "null"]]]"}

 

Field error in object 'batchErrorCollectFormDto' on field 'endDttm': rejected value [null]; 
codes [typeMismatch.batchErrorCollectFormDto.endDttm,typeMismatch.endDttm,typeMismatch.java.lang.Long,typeMismatch]; 
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [batchErrorCollectFormDto.endDttm,endDttm]; 
arguments []; 
default message [endDttm]]; 
default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Long' for property 'endDttm'; 
nested exception is java.lang.NumberFormatException: For input string: "null"]

 

null이 들어왔다고 까인듯 하다.

그래, 이전 화면에서는 화면을 먼저 구성하느라 null 은 들어오지 않았던것 같다. 

테스트로 요청케이스를 만들거나, 다시 화면부터 작업해야겠다. 

 

https://noritersand.github.io/spring/spring-org-springframework-validation-bindexception/

 

[Spring] org.springframework.validation.BindException

띠용

noritersand.github.io

 


* 작업중인 소스목록을 참고하고 싶을때가 있다.

# 실패 - 단일파일 선택시 파일경로를 참조할 수 있으나, 여러파일 선택시 불가
 - copy path to clipboard

# 성공 - 상단에 표시 모드를 전환하면 여러 파일 선택 시 ctrl+c 로 목록 복사 가능
 - Flat list (multiple column)

유레카.

'etc' 카테고리의 다른 글

svn 설치(at window), 프로젝트 소스 commit  (0) 2024.01.26

https://lahuman.jabsiri.co.kr/158

 

[TIP] JAVA CODE STYLE GUIDE

JAVA CODE STYLE GUIDE 코드 스타일 가이드가 있으면 결과적으로 코드의 질이 좋아진다. (당장은 아니겠지만) 기본적으로 많은 곳에서 사용되는 자바 스타일 가이드는 SUN 의 스타일 가이드이다. 스타

lahuman.jabsiri.co.kr

 

* SUN style guide

* Google style guide

 - javaguide.html

 - Google style guide for Eclipse

 - ECLIPSE를 실행하고 다음 메뉴로 이동한다.   
   > Window > Preference > java > Code Style > Formatter
 - Import를 선택하고 내려받은 XML 파일을 선택하고 Active Profile을 GoogleStyle로 설정

 

* 오류확인

 - https://github.com/google/styleguide/issues/338

javaguide.htm
0.27MB
htmlcssguide.xml
0.14MB
eclipse-java-google-style.xml
0.03MB
javascriptguide.xml
1.04MB
jsoncstyleguide.xml
0.45MB

 

* 추가 eclipse 설정

 

* 블럭 주석 내용 formating([C]+[S]+f) 대상 제외

 - Window - Preference - Java - Code Style - Formatter - New(새로운 프로필 생성) - Edit - Comments

 > Enable block comment formatting 체크 해제

QNAP NAS 렌섬웨어 파일 복구

 

QNAP NAS 렌섬웨어 파일 복구

얼마전 사용중인 qnap nas가 렌섬웨어에 걸렸다. 당황해서 이것저것 알아보다가 손을 놓고 있었는데, qnap 측에서 파일복구방법을 공지했다. Qlocker 공격에서 파일을 복구하는 방법 당황스러운 것은

jkoogi.tistory.com

QNAP 나스 사용중에 발생한 렌섬웨어 복구 과정에서

복구프로그램을 이용한 복구 이후 

감염파일과 복구파일 정리 과정을 java로 프로그램하여 처리해봤다. 

 

일단 QNAP NAS에 JAVA를 설치해야한다.

 

App Center에 등록된 JRE 8.151.2.1 을 설치했다.

java 설치확인 및 java 프로그램 실행을 위해 터미널 환경을 열어줘야 했다.

제어판 > 네트워크 및 파일 서비스 > Telnet / SSH

 

체크 : SSH 연결 허용(관리자만 원격으로 로그인할 수 있습니다.)

 

https://www.myqnapcloud.com/ 에 접속하여 디바이스메니저 목록으로 접속한 나스의

브라우저 주소창에 표시된 접속IP로 터미널 접속을 한다.

 - command 프로그램 이용 : MobaXterm 프로그램을 사용. (좋다고 하는데, 한글이 잘 표현되지 않는듯.)

 > IP, username, prot를 입력하고 접속 (비밀번호 입력)

왼쪽 소스트리 페널을 이용하여 root[/] 로 이동하면서 NAS의 파일시스템 구조를 확인할 수 있었다.

 - 시작시 Q > Y 를 입력하여 프롬프트로 이동

 

앞서 설치한 jre 확인

 - java -version

프로그램을 업로드하고 실행한다.

터미널에서 확인

 


프로그램 : ransonwareFix.jar

 - 복구대상 경로, 복구완료 경로를 지정하면

 - 복구대상 경로의 하위폴더를 순회하면서 감염된 파일이 폴더에 없으면 '!!!READ_ME.txt' 삭제

 - 감염된 파일이 존재할 경우 해당 경로를 복구완료 경로에서 복구완료파일을 확인하여 존재하면 복구대상 폴더로 복사

 - 복구대상 폴더로 복구파일을 복사한 경우, 해당 감염파일 삭제

 - 복구완료 폴더에 파일이 복구할 파일이 없을경우 폴더 삭제

 

* 기본설정 옵션

 - 복구대상 : /share/CACHEDEV1_DATA/

 - 복구완료 : /share/external/DEV3301_1/restore1/share/CACHEDEV1_DATA/

 > java -jar /share/CACHEDEV1_DATA/work/workspace/ransonwareFix.jar

* 두번째 복구 옵션

 - 복구대상 : /share/CACHEDEV4_DATA/

 - 복구완료 : /share/external/DEV3301_1/restore4/share/CACHEDEV4_DATA/

 > java -jar /share/CACHEDEV1_DATA/work/workspace/ransonwareFix.jar /share/CACHEDEV4_DATA/ /share/external/DEV3301_1/restore4/share/CACHEDEV4_DATA/

* 세번째 복구 옵션

 - 복구대상 : /share/CACHEDEV5_DATA/

 - 복구완료 : /share/external/DEV3301_1/restore5/share/CACHEDEV5_DATA/

 > java -jar /share/CACHEDEV1_DATA/work/workspace/ransonwareFix.jar /share/CACHEDEV5_DATA/ /share/external/DEV3301_1/restore5/share/CACHEDEV5_DATA/

'nas' 카테고리의 다른 글

QNAP NAS 렌섬웨어 파일 복구  (0) 2021.10.07
QNAP HDD 교체.  (0) 2021.02.21

https://kitty-geno.tistory.com/89

 

Eclipse | git-receive-pack not permitted on

GitHub 연동 후 문제없이 사용하고 있었는데 갑자기 Commit, Push, Pull.. 아래와 같은 에러 메시지가 나왔다. 결론은 2021. 08. 13일부터 GitHub에서 ID/Password 인증을 없애고 ID/Personal Access Token 방식의..

kitty-geno.tistory.com

 

2021.08.31 부터 GitHub에서 id/pw  인증을 없애고

access token 방식으로 전환했다고 한다. 

 

1. GitHub 홈페이지 > Github Signed > Settings(우상단 프로필)

2. Developer settings 선택

3. Personal access tokens > Generate new token

4. 토큰이름 작성, 만료일 선택(유효기간), 허용범위 선택

5. token을 저장한다. (Personal access tokens)

6. eclipse

 - Git Repositories > Remotes > origin > 해당하는 Github 우클릭 > Change Credentials...

 > github Id/token 입력

7. Commit And Push 정상 동작 확인

'tools' 카테고리의 다른 글

ecilpse 속도 개선  (1) 2023.09.12
vsCode git pull request  (0) 2023.04.16
오류  (0) 2022.10.24
VSCode Workspace Trust?  (0) 2021.08.27
Visual Studio Code  (0) 2021.06.20

 

얼마전 사용중인 qnap nas가 렌섬웨어에 걸렸다.

당황해서 이것저것 알아보다가 손을 놓고 있었는데,

qnap 측에서 파일복구방법을 공지했다.

 

Qlocker 공격에서 파일을 복구하는 방법

당황스러운 것은 동일한 크기의 복구디스크를 준비해서 외장하드로 연결해야 한다는 것이다.

 

그래서.

마늘을 설득해서 8T 하드를 하나 더 샀다 ㅠ.ㅠ

(복구하고 되팔이로 금전적 손해를 없게 하겠다는 계획을 호소해야 했다. )

 

더보기

예전에 1T를 쓰던 외장하드가 있어서 하드만 사면 교체하고 사용할 수 있을 줄 알았는데

너무 오랜만에 꺼내서였는지... 전원을 연결하자마자 콘덴서가 터지면서 

외장하드 케이스가 망가지고 말았다.

 

그래서, 외장하드를 추가로 구매하였는데, 구매하면서 알게된 것이

8T 사용이 가능한 외장하드 케이스를 고려해서 구매했어야 한다는 거다. 

여러가지로.. 그냥되는게 없다.

 

8T HDD를 외장하드 케이스에 연결하고 안내에 따라 진행하는데, 

데모영상도 참고하면서 하니 큰 어려움이 없이 진행되었다.

 

그런데...


QRescue 설치.

NAS에서는 제공하는 공식 앱이 아닌 렌섬웨어 복구용 프로그램을 별도 배포했다. 

그래서 QRescue 라는 프로그램을 클라이언트 PC에 다운로드 받아서 

NAS의 AppCenter의 사용자 설치 기능으로 QRescue를 설치한다.

(외장하드가 연결되어야 앱이 설치된다.)

 

1. 외장하드설치, 앱설치, 복구 폴더 생성은 가이드 참고.

 

그런데, 렌섬웨어 파일 복구를 위해 별도 제공하는 QRescue 프로그램을 실행하니 

가이드와 다른 메뉴가 나왔다. 

  +-------------------------------------------------------------------------+
  |  Console Management - Main menu                                         |
  |                                                                         |
  |  1: Show network settings                                               |
  |  2: System event logs                                                   |
  |  3: Reset to factory default (password required)                        |
  |  4: Activate/ deactivate a license                                      |
  |  5: App management                                                      |
  |  6: Reboot in Rescue mode (w/o configured disk)                         |
  |  7: Reboot in Maintenance Mode                                          |
  |  Q: Quit (return to normal shell environment)                           |
  +-------------------------------------------------------------------------+
  >>

이것저것 찾아보다가 Q > Y 를 입력해 보니 가이드에 나오는 프롬프트가 나왔다. ㅡ.ㅡ

가이드에 나온 프롬프트 : [admin@서버 qrescue] #

 - 특이한 것은 다른 계정으로 로그인한 크롬 브라우저에서 QRescue를 조작하니 admin계정으로 로그인한 크롬 브라우저에 동일하게 적용이 되었다.

 - QRescue 프롬프트에서 exit를 실행하니, 서비스가 종료되어 버렸다. 결국 설치한 앱을 정지한 뒤 앱을 다시 시작하고, 실행하니 복구가 됐다.

 

2. photorec 명령으로 렌섬웨어 파일을 복구하자.

 - 상하 방향키로 복구할 디스크 논리디스크 선택, 좌우 방향키로 Proceed 선택하여 Enter.

복구 디스크 선택 후 Proccessd 가 선택되지 않으면(Enter로 진행되지 않을 경우)

photorec를 다시 실행하고 선택한다. (쓰레드가 끊기는 현상인듯)

[Whole] 선택

[..] 항목을 선택하여 share 폴더까지 상위로 폴더로 이동.

 - share 폴더에서 외장하드의 레이블로 설정한 rescue 항목으로 이동하여 복구 대상 폴더 선택

 > /dev/mapper/cachedev1 > /share/rescue/recup1

 (이리저리방황하다가 '/share/external/DEV3301_1/recup1/' 잘못된 경로를 지정하면 실패)

 - 복구파일을 위치할 대상 폴더까지 이동하여 'C' 입력

첫번째 디스크로 OS 디스크를 선택했는데, 체크하는데 17시간? 부터 줄어들더니

 - 8시간 40분 소요된다고 표시되었다.

자고일어나니 이렇게 정리되어 있었다.

복구대상 검사가 끝나고, 파일이 옮겨진 것 같다.

다음 스텝 준비


3. QRescue 를 이용하여 photorec 에서 검색한 파일을 복구한다.

 - qrescue.sh

 > recup1 폴더에 restore(번호) 폴더를 생성하여 파일을 복구함.

여러 디스크가 존재할 경우 디스크를 선택한다.

복구 로그가 표시된다.

복구대상 취합 폴더  : recup1

복구된 파일 확인 : restore1

파일들이 복구됐다. qrescue 를 실행하니 폴더구조까지 유지되며 복구가 되었다. 

 - 복구파일과 원본파일.. 이중으로 용량을 차지하는건가?

* 복구파일 위치
rescue > recup1 > recup_dir.1 > 복구파일 위치 (일괄파일명?)

* 원본 스토리지 구조
rescue > restore1 > share > CACHEDEV1_DATA > 이하 원본 스토리지 구조(원본파일명)

하지만, 일부 파일이 누락된 것을 확인했다.

 

불안한데.. (일단 다 확인할 수는 없고)

 

복구된 파일을 원래 폴더에 이동하고

렌섬웨어에 걸린 파일을 삭제한 뒤

[!!!READ_ME.txt] 파일을 삭제하는 작업을 진행해야 한다.

 

... 귀찮다. 

파일병합 프로그램을 만들어보자.

 

만들었다.

restore1 폴더에 복구된 파일을

 - 원래 폴더로 이동하고,

 - 감염파일을 삭제한 뒤 

 - 복구폴더가 빈상태가 되면 폴더를 지웠다.

(프로그램을 돌리니 일부 빈폴더가 남아서 수동으로 지워줬다.)

 

한 싸이클을 돌고나니, 복구되지 않은 파일들을 대상으로

한번 더 돌리면 복구될지.. 궁금해졌다.

 

돌려보자.

 - 자고일어나니 60만건이나 또 취합되었다. 

 > 반복하면 다 복구할 듯 한데.. 한번에 8시간씩 걸리니 언제 끝나려나 ㅍ

(그래도 걸렸을때의 막막함 보다는 희망적이다. 파일배치 프로그램도 만들어보고. ㅋ)

1번 스토리지 2차 복구결과

679058 files saved in /share/rescue/recup1/recup_dir directory.
Recovery completed.

 > 541450건(1차) + 679058건(2차) = 총 1,220,508 건

오류가났다.

파일이 없다고 오류가 났다.

 - '/share/rescue/recup1/recup_dir.1059/f3783530944.jpg'

 > 해당경로에 파일이 없었다. 프로그램 파일만 있는듯 한데... 뭘 잘못했지?

... 설치한 앱을 정지하고 실행한뒤 photorec를 실행해봤다.


여기까지 하고, QNAP에 서비스지원 티켓을 요청했다.

답변이 오면 다시 시도해보자.

왔다.

- 실망이 크다. 복구시 암호를 추출하지 않았던거구나...

 

 

 


같은 디스크 재실행은 포기하고,

두번째 디스크를 실행했다.

 

67시간이 걸린다고?

220시간이 걸린다고? ㅡ.ㅡ

시간이 널을 뛰는구나...

 

두번째 디스크의 복구는 71038 파일이 복구되었단다.

 

복구대상으로 생성된 'recup_dir.1' 패턴의 폴더들이 

/share/rescue/recup3 에 생성이 되었어야 하는데

/share/rescue 폴더에 잘못생겼다.

 - 생성된 143개의 폴더를 recup3폴더로 이동하니 qrescue.sh가 실행되었다.

 

아마 C 를 눌러서 실행하는 화면에서 

/. 항목이 선택된 상태로 C를 눌러 상위 폴더에 생성된 것인가 싶어

다시 /.. 항목이 선택된 상태로 시도를 해보았다.

 

.. 이래저래 다 실패.

 


QRescue 서비스를 정지했다가 다시 실행해서

첫번째 디스크부터 다시 실행해본다.

 

여기서 오래걸리는걸 보니..될라나?

... 는 개뿔... 실패

 

 

 


두번째 스토리지 (sub)를 복구하며

qrescue.sh 실행시 3번을 선택하니 복구가 시작되었다.

... 그런데, 하나걸러 하나 skip 하는듯...

 

 


세번째 디스크

 

아.. 오래걸린다.

 - 복구가 된다니 좋은건가. 그래도 28시간은.. 좀..

 

 

복구를 해보자

... 뭐꼬? 왜 안되나?

 

'nas' 카테고리의 다른 글

QNAP 렌섬웨어 복구후 파일정리  (4) 2021.10.30
QNAP HDD 교체.  (0) 2021.02.21

# React-19.4. create 구현 - form

 

1. CreateContent 컴포넌트에 글작성 폼 추가

1.1 제목, 내용, 버튼 추가

: CreateContent.js - retun 구문에 form 테그 추가
<form>
  <p>
    <input type="text" name="title"
    placeholder="title"></input>
  </p>
  <p>
    <textarea name="desc" placeholder="description"></textarea>
  </p>
  <p>
    <input type="submit"></input>
  </p>
</form>

1.2 form 액션 설정

 - 서브밋 버튼(제출) 클릭시 메시지창을 띄우며 화면전환이 되지 않도록 작성

: CreateContent.js - form 테그 속성 정의
<form action="/create_process" method="post"
  onSubmit={function(e){
    e.preventDefault();
    alert('submit!!');
  }.bind(this)}
>

 

* 다음 기능

 - app.js 컴포넌트의 content 끝에 form 에서 작성한 사용자의 입력정보를 추가

# React-19.3. create 구현 -  mode 전환 기능

 

1. create 클릭시 Content 영역 변경

1.1 조회시 기본 화면 : 읽기(read) 모드에 해당하는 Content 화면(HTML, CSS, JavaScript..) 표시

 > 진행을 위해 Content > ReadContent 로 변경 (초기에 작성한 Article 컴포넌트를 Content로 진행함)

: app.js - import 구문 (Article.js에는 정의하지 않은 export 설정)
import ReadContent from './components/ReadContent';

: app.js - render() Article 구분 
<ReadContent title={_title} desc={_desc}></ReadContent>
: ReadContent.js - Article.js(Content.js) 파일명 및 컴포넌트 구조 변경
import React, { Component } from 'react';

export class ReadContent extends Component {
  render() {
    // this.props.title = 'hi ';
    console.log("ReadContent render");
    return (
      <article>
        <h2>HTML</h2>
        {this.props.desc}
      </article>
    );
  }
}

export default ReadContent;

1.2 create 클릭시 Create 화면으로 변경

 - CreateContent 컴포넌트 추가 (이전작업으로 create 클릭 시 mode가 create로 변경되는 상태)

 > mode 변경에 따라 ReadContent 를 CtreatContent 로 변경 

1.2.1 app.js에서 ReadContent 로  return 하던 구문을 동적으로 상황(create)에 맞게 표시하기 위해

  _article 변수를 추가하여 ReadContent 표시구문을 적용

: app.js - return에서 ReadContent 구문 동적구성
{_article}

1.2.2 추출된 _article 변수는 render 처리를 위한 변수로 선언하고, 

 mode 에 따른 표시정보를 구성하는 로직에 ReadContent 구문을 적용

: app.js - _article 변수 선언(_article 추가)
var _title, _desc, _article = null;

: app.js - render() 구성시 기존 ReadContent 적용 로직
if(this.state.mode === 'welcome'){
  ...
  _article = <ReadContent title={_title} desc={_desc}></ReadContent>
}else if(this.state.mode === 'read'){
  ...
  _article = <ReadContent title={_title} desc={_desc}></ReadContent>
}

1.2.3 변경사항 확인

 - 기능의 변화 없이 _article로 추출한 ReadContent 정상표시여부 확인

1.2.4 mode 값이 create 로 변경될 경우 _article 영역에 CreateContent 표시

 - import에 CreateContent 추가

 - state.mode 가 create 인 경우 _article 영역에 CreateContent 표시

: app.js - import 구문추가 (CreateContent)
import CreateContent from './components/CreateContent';

: app.js - render()의 mode 별 처리로직에 CreateContent 표시로직 추가

if(this.state.mode === 'welcome'){
  ...
  _article = <ReadContent title={_title} desc={_desc}></ReadContent>
}else if(this.state.mode === 'read'){
  ...
  _article = <ReadContent title={_title} desc={_desc}></ReadContent>  
}else if(this.state.mode === 'create'){
  _article = <CreateContent></CreateContent>
}

 

 

* 다음 기능

 - CreateContent의 form 영역 구성

# React-19.2. create 구현 -  mode 변경 기능

 

1. button 영역 추가

: src\App.js - rander의 toc 컴포넌트 다음에 버튼영역 추가
<ul>
  <li><a href="/create">create</a></li>
  <li><a href="/update">update</a></li>
  <li><input type="button" value="delete"  ></input></li>
</ul>

2. 추가한 button 영역을 컴포넌트로 추출 : Control

: app.js - import 영역 추가
import Control from './components/Control';

: app.js - rander 영역 추출코드로 변경
<Control></Control>
: Control.js 추가
import React, { Component } from 'react';

export class Control extends Component {
  render() {
    console.log("Control render");
    return (
        <ul>
          <li><a href="/create">create</a></li>
          <li><a href="/update">update</a></li>
          <li><input type="button" value="delete"></input></li>
        </ul>
    );
  }
}

export default Control; // app.js에서 중괄호 없이 import 가능

3. app.js에서 Control 영역에 그려진 li 항목 선택시 이벤트 발생

 - Control 클릭시 app.js의 mode 정보 수정

3.1 Control 컨트롤러에 이벤트를 추가하여 app.js에 정의될 사용자 이벤트함수 호출

: Control.js - return에 정의된 항목에 이벤트 추가

<ul>
    <li><a href="/create" onClick={function(e){
        e.preventDefault();
        this.props.onChangeMode('create');
    }.bind(this)}>create</a></li>
    <li><a href="/update" onClick={function(e){
        e.preventDefault();
        this.props.onChangeMode('update');
    }.bind(this)}>update</a></li>
    <li><input  onClick={function(e){
        e.preventDefault();
        this.props.onChangeMode('delete');
    }.bind(this)}type="button" value="delete"></input></li>
</ul>

3.2 Control 컨트롤러 클릭시 호출할 사용자 이벤트 함수 정의

: app.js - return에서 호출하는 Control에 사용자 이벤트 함수 추가

<Control onChangeMode={function(_mode){
  this.setState({
    mode : _mode
  });
}.bind(this)}></Control>

 

* 다음 기능

 - mode(create, update, delete) 값에 따라 Content 표시영역 변경되도록 수정

+ Recent posts